Adds class implementation for use STM32F4xx OTG_HS in FS mode

Dependents:   IGLOO_board

Fork of USBDevice by mbed official

Revision:
71:53949e6131f6
diff -r 2c525a50f1b6 -r 53949e6131f6 targets/TARGET_Silicon_Labs/src/em_usbd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_Silicon_Labs/src/em_usbd.c	Thu Jul 27 12:14:04 2017 +0100
@@ -0,0 +1,1438 @@
+/**************************************************************************//**
+ * @file em_usbd.c
+ * @brief USB protocol stack library, device API.
+ * @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"
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+static USBD_Device_TypeDef device;
+USBD_Device_TypeDef *dev = &device;
+
+static uint32_t totalRxFifoSize = 0, totalTxFifoSize = 0;
+static int numEps = 0;
+static int txFifoNum = 1;
+
+static void USBD_ResetEndpoints(void);
+extern USB_Status_TypeDef USBDHAL_ReconfigureFifos( uint32_t totalRxFifoSize,
+                                                    uint32_t totalTxFifoSize );
+#ifndef __MBED__
+static const char *stateNames[] =
+{
+  [ USBD_STATE_NONE       ] = "NONE      ",
+  [ USBD_STATE_ATTACHED   ] = "ATTACHED  ",
+  [ USBD_STATE_POWERED    ] = "POWERED   ",
+  [ USBD_STATE_DEFAULT    ] = "DEFAULT   ",
+  [ USBD_STATE_ADDRESSED  ] = "ADDRESSED ",
+  [ USBD_STATE_CONFIGURED ] = "CONFIGURED",
+  [ USBD_STATE_SUSPENDED  ] = "SUSPENDED ",
+  [ USBD_STATE_LASTMARKER ] = "UNDEFINED "
+};
+#endif
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ *   Abort all pending transfers.
+ *
+ * @details
+ *   Aborts transfers for all endpoints currently in use. Pending
+ *   transfers on the default endpoint (EP0) are not aborted.
+ ******************************************************************************/
+void USBD_AbortAllTransfers( void )
+{
+  INT_Disable();
+  USBDHAL_AbortAllTransfers( USB_STATUS_EP_ABORTED );
+  INT_Enable();
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Abort a pending transfer on a specific endpoint.
+ *
+ * @param[in] epAddr
+ *   The address of the endpoint to abort.
+ ******************************************************************************/
+int USBD_AbortTransfer( int epAddr )
+{
+  USB_XferCompleteCb_TypeDef callback;
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( ep->num == 0 )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  INT_Disable();
+  if ( ep->state == D_EP_IDLE )
+  {
+    INT_Enable();
+    return USB_STATUS_OK;
+  }
+
+  USBD_AbortEp( ep );
+
+  ep->state = D_EP_IDLE;
+  if ( ep->xferCompleteCb )
+  {
+    callback = ep->xferCompleteCb;
+    ep->xferCompleteCb = NULL;
+
+    if ( ( dev->lastState == USBD_STATE_CONFIGURED ) &&
+         ( dev->state     == USBD_STATE_ADDRESSED  )    )
+    {
+      USBDHAL_DeactivateEp( ep );
+    }
+
+    DEBUG_TRACE_ABORT( USB_STATUS_EP_ABORTED );
+    callback( USB_STATUS_EP_ABORTED, ep->xferred, ep->remaining );
+  }
+
+  INT_Enable();
+  return USB_STATUS_OK;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Start USB device operation.
+ *
+ * @details
+ *   Device operation is started by connecting a pullup resistor on the
+ *   appropriate USB data line.
+ ******************************************************************************/
+void USBD_Connect( void )
+{
+  INT_Disable();
+  USBDHAL_Connect();
+  INT_Enable();
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Stop USB device operation.
+ *
+ * @details
+ *   Device operation is stopped by disconnecting the pullup resistor from the
+ *   appropriate USB data line. Often referred to as a "soft" disconnect.
+ ******************************************************************************/
+void USBD_Disconnect( void )
+{
+  INT_Disable();
+  USBDHAL_Disconnect();
+  INT_Enable();
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Check if an endpoint is busy doing a transfer.
+ *
+ * @param[in] epAddr
+ *   The address of the endpoint to check.
+ *
+ * @return
+ *   True if endpoint is busy, false otherwise.
+ ******************************************************************************/
+bool USBD_EpIsBusy( int epAddr )
+{
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_EpIsBusy(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( ep->state == D_EP_IDLE )
+    return false;
+
+  return true;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Get current USB device state.
+ *
+ * @return
+ *   Device USB state. See @ref USBD_State_TypeDef.
+ ******************************************************************************/
+USBD_State_TypeDef USBD_GetUsbState( void )
+{
+  return dev->state;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Get a string naming a device USB state.
+ *
+ * @param[in] state
+ *   Device USB state. See @ref USBD_State_TypeDef.
+ *
+ * @return
+ *   State name string pointer.
+ ******************************************************************************/
+const char *USBD_GetUsbStateName( USBD_State_TypeDef state )
+{
+  if ( state > USBD_STATE_LASTMARKER )
+    state = USBD_STATE_LASTMARKER;
+
+#ifndef __MBED__
+  return stateNames[ state ];
+#else
+  return NULL;
+#endif
+}
+
+/***************************************************************************//**
+ * @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;
+
+#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) );
+
+  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;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Start a read (OUT) transfer on an endpoint.
+ *
+ * @note
+ *   The transfer buffer length must be a multiple of 4 bytes in length and
+ *   WORD (4 byte) aligned. When allocating the buffer, round buffer length up.
+ *   If it is possible that the host will send more data than your device
+ *   expects, round buffer size up to the next multiple of maxpacket size.
+ *
+ * @param[in] epAddr
+ *   Endpoint address.
+ *
+ * @param[in] data
+ *   Pointer to transfer data buffer.
+ *
+ * @param[in] byteCount
+ *   Transfer length.
+ *
+ * @param[in] callback
+ *   Function to be called on transfer completion. Supply NULL if no callback
+ *   is needed. See @ref USB_XferCompleteCb_TypeDef.
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_Read( int epAddr, void *data, int byteCount,
+               USB_XferCompleteCb_TypeDef callback )
+{
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  USB_PRINTF("USBD: Read addr %x, data %p, size %d, cb 0x%lx\n",
+             epAddr, data, byteCount, (uint32_t)callback);
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( (   byteCount > MAX_XFER_LEN                           ) ||
+       ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER )    )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal transfer size" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( (uint32_t)data & 3 )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Misaligned data buffer" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  INT_Disable();
+  if ( USBDHAL_EpIsStalled( ep ) )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is halted" );
+    return USB_STATUS_EP_STALLED;
+  }
+
+  if ( ep->state != D_EP_IDLE )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is busy" );
+    return USB_STATUS_EP_BUSY;
+  }
+
+  if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Device not configured" );
+    return USB_STATUS_DEVICE_UNCONFIGURED;
+  }
+
+  ep->buf       = (uint8_t*)data;
+  ep->remaining = byteCount;
+  ep->xferred   = 0;
+
+  if ( ep->num == 0 )
+  {
+    ep->in = false;
+  }
+  else if ( ep->in != false )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal EP direction" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  ep->state          = D_EP_RECEIVING;
+  ep->xferCompleteCb = callback;
+
+  USBD_ArmEp( ep );
+  INT_Enable();
+  return USB_STATUS_OK;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Perform a remote wakeup signalling sequence.
+ *
+ * @note
+ *   It is the responsibility of the application to ensure that remote wakeup
+ *   is not attempted before the device has been suspended for at least 5
+ *   miliseconds. This function should not be called from within an interrupt
+ *   handler.
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_RemoteWakeup( void )
+{
+  INT_Disable();
+
+  if ( ( dev->state != USBD_STATE_SUSPENDED ) ||
+       ( dev->remoteWakeupEnabled == false  )    )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_RemoteWakeup(), Illegal remote wakeup" );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  USBDHAL_SetRemoteWakeup();
+  INT_Enable();
+  USBTIMER_DelayMs( 10 );
+  INT_Disable();
+  USBDHAL_ClearRemoteWakeup();
+  INT_Enable();
+  return USB_STATUS_OK;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Check if it is ok to enter energy mode EM2.
+ *
+ * @note
+ *   Before entering EM2 both the USB hardware and the USB stack must be in a
+ *   certain state, this function checks if all conditions for entering EM2
+ *   is met.
+ *   Refer to the @ref usb_device_powersave section for more information.
+ *
+ * @return
+ *   True if ok to enter EM2, false otherwise.
+ ******************************************************************************/
+bool USBD_SafeToEnterEM2( void )
+{
+#if ( USB_PWRSAVE_MODE )
+  return USBD_poweredDown ? true : false;
+#else
+  return false;
+#endif
+}
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+void USBD_SetUsbState( USBD_State_TypeDef newState )
+{
+  USBD_State_TypeDef currentState;
+
+  currentState = dev->state;
+  if ( newState == USBD_STATE_SUSPENDED )
+  {
+    dev->savedState = currentState;
+  }
+
+  dev->lastState = dev->state;
+  dev->state = newState;
+
+  if ( ( dev->callbacks->usbStateChange ) &&
+       ( currentState != newState       )    )
+  {
+    /* When we transition to a state "lower" than CONFIGURED
+     * we must reset the endpoint data
+     */
+    if ( (dev->lastState == USBD_STATE_CONFIGURED ||
+          dev->lastState == USBD_STATE_SUSPENDED ) &&
+         dev->state < USBD_STATE_CONFIGURED )
+    {
+      USBD_ResetEndpoints();
+    }
+
+    dev->callbacks->usbStateChange( currentState, newState );
+  }
+}
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ *   Set an endpoint in the stalled (halted) state.
+ *
+ * @param[in] epAddr
+ *   The address of the endpoint to stall.
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_StallEp( int epAddr )
+{
+  USB_Status_TypeDef retVal;
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_StallEp(), Illegal request" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( ep->num == 0 )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_StallEp(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  INT_Disable();
+  retVal = USBDHAL_StallEp( ep );
+  INT_Enable();
+
+  if ( retVal != USB_STATUS_OK )
+  {
+    retVal = USB_STATUS_ILLEGAL;
+  }
+
+  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();
+  USBHAL_DisableUsbInt();
+  USBHAL_DisablePhyPins();
+  USBD_SetUsbState( USBD_STATE_NONE );
+  /* Turn off USB clocks. */
+  CMU->HFCORECLKEN0 &= ~(CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC);
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Reset stall state on a stalled (halted) endpoint.
+ *
+ * @param[in] epAddr
+ *   The address of the endpoint to un-stall.
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_UnStallEp( int epAddr )
+{
+  USB_Status_TypeDef retVal;
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal request" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( ep->num == 0 )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  INT_Disable();
+  retVal = USBDHAL_UnStallEp( ep );
+  INT_Enable();
+
+  if ( retVal != USB_STATUS_OK )
+  {
+    retVal = USB_STATUS_ILLEGAL;
+  }
+
+  return retVal;
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Start a write (IN) transfer on an endpoint.
+ *
+ * @param[in] epAddr
+ *   Endpoint address.
+ *
+ * @param[in] data
+ *   Pointer to transfer data buffer. This buffer must be WORD (4 byte) aligned.
+ *
+ * @param[in] byteCount
+ *   Transfer length.
+ *
+ * @param[in] callback
+ *   Function to be called on transfer completion. Supply NULL if no callback
+ *   is needed. See @ref USB_XferCompleteCb_TypeDef.
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_Write( int epAddr, void *data, int byteCount,
+                USB_XferCompleteCb_TypeDef callback )
+{
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  USB_PRINTF("USBD: Write addr %x, data %p, size %d, cb 0x%lx\n",
+             epAddr, data, byteCount, (uint32_t)callback);
+
+  if ( ep == NULL )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal endpoint" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( (   byteCount > MAX_XFER_LEN                           ) ||
+       ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER )    )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal transfer size" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  if ( (uint32_t)data & 3 )
+  {
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Misaligned data buffer" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  INT_Disable();
+  if ( USBDHAL_EpIsStalled( ep ) )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is halted" );
+    return USB_STATUS_EP_STALLED;
+  }
+
+  if ( ep->state != D_EP_IDLE )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is busy" );
+    return USB_STATUS_EP_BUSY;
+  }
+
+  if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Device not configured" );
+    return USB_STATUS_DEVICE_UNCONFIGURED;
+  }
+
+  ep->buf       = (uint8_t*)data;
+  ep->remaining = byteCount;
+  ep->xferred   = 0;
+
+  if ( ep->num == 0 )
+  {
+    ep->in = true;
+  }
+  else if ( ep->in != true )
+  {
+    INT_Enable();
+    DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal EP direction" );
+    EFM_ASSERT( false );
+    return USB_STATUS_ILLEGAL;
+  }
+
+  ep->state          = D_EP_TRANSMITTING;
+  ep->xferCompleteCb = callback;
+
+  USBD_ArmEp( ep );
+  INT_Enable();
+  return USB_STATUS_OK;
+}
+
+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
+ *   Query the stall state of an endpoint
+ *
+ * @param[in] epAddr
+ *   The address of the endpoint to query.
+ *
+ * @return
+ *   True if endpoint is stalled, false otherwise
+ ******************************************************************************/
+int USBD_EpIsStalled(int epAddr)
+{
+  USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+
+  if( !ep )
+  {
+    return false;
+  }
+
+  return USBDHAL_EpIsStalled(ep);
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Reset (remove) all client endpoints
+ *
+ * @details
+ *   Removes client endpoints, and resets the RX/TX fifos. No endpoints
+ *   other than EP0 can be used until added with @ref USBD_AddEndpoint.
+ ******************************************************************************/
+static void USBD_ResetEndpoints(void)
+{
+  USBD_Ep_TypeDef *ep = &dev->ep[0];
+
+  numEps = 0;
+  txFifoNum = 1;
+
+  totalTxFifoSize  = ep->fifoSize * 1;
+  totalRxFifoSize  = (ep->fifoSize + 1) * 1;
+  totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) );
+}
+
+/***************************************************************************//**
+ * @brief
+ *   Add a new endpoint
+ *
+ * @param[in] epAddr
+ *   Endpoint address
+ *
+ * @param[in] transferType
+ *   Endpoint type, one of @ref USB_EPTYPE_BULK, @ref USB_EPTYPE_INTR or
+ *   @ref USB_EPTYPE_ISOC.
+ *
+ * @param[in] maxPacketSize
+ *   Maximum packet size of the new endpoint, in bytes
+ *
+ * @param[in] bufferMult
+ *   FIFO buffer size multiplier
+ *
+ * @return
+ *   @ref USB_STATUS_OK on success, else an appropriate error code.
+ ******************************************************************************/
+int USBD_AddEndpoint(int epAddr, int transferType,
+                     int maxPacketSize, int bufferMult)
+{
+  USBD_Ep_TypeDef *ep;
+
+  numEps++;
+
+  ep                 = &dev->ep[ numEps ];
+  ep->in             = ( epAddr & USB_SETUP_DIR_MASK ) != 0;
+  ep->buf            = NULL;
+  ep->addr           = epAddr;
+  ep->num            = ep->addr & USB_EPNUM_MASK;
+  ep->mask           = 1 << ep->num;
+  ep->type           = transferType;
+  ep->packetSize     = maxPacketSize;
+  ep->remaining      = 0;
+  ep->xferred        = 0;
+  ep->state          = D_EP_IDLE;
+  ep->xferCompleteCb = NULL;
+
+  if ( ep->in )
+  {
+    ep->txFifoNum = txFifoNum++;
+    ep->fifoSize = ( ( ep->packetSize + 3 ) / 4 ) * bufferMult;
+    dev->inEpAddr2EpIndex[ ep->num ] = numEps;
+    totalTxFifoSize += ep->fifoSize;
+
+    if ( ep->num > MAX_NUM_IN_EPS )
+    {
+      DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal IN EP address" );
+      EFM_ASSERT( false );
+      return USB_STATUS_ILLEGAL;
+    }
+  }
+  else
+  {
+    ep->fifoSize = ( ( ( ep->packetSize + 3 ) / 4 ) + 1 ) * bufferMult;
+    dev->outEpAddr2EpIndex[ ep->num ] = numEps;
+    totalRxFifoSize += ep->fifoSize;
+
+    if ( ep->num > MAX_NUM_OUT_EPS )
+    {
+      DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal OUT EP address" );
+      EFM_ASSERT( false );
+      return USB_STATUS_ILLEGAL;
+    }
+  }
+
+  USB_PRINTF("USBD: Added endpoint %d to slot %d, in %d, addr 0x%x, type %d, ps %d, fifo %ld (total tx %ld, rx %ld)\n",
+             ep->num, numEps, ep->in, ep->addr, ep->type, ep->packetSize, ep->fifoSize,
+             totalTxFifoSize, totalRxFifoSize);
+
+  INT_Disable();
+#if defined( CMU_OSCENCMD_USHFRCOEN )
+  /* Happy Gecko workaround: disable LEM GATE mode if using ISOC endpoints. */
+  if ( transferType == USB_EPTYPE_ISOC )
+  {
+      USB->CTRL = (USB->CTRL & ~_USB_CTRL_LEMOSCCTRL_MASK) | USB_CTRL_LEMOSCCTRL_NONE;
+  }
+#endif
+
+  int ret = USBDHAL_ReconfigureFifos(totalRxFifoSize, totalTxFifoSize);
+  INT_Enable();
+
+  if( ret != USB_STATUS_OK ) {
+    return ret;
+  }
+
+  USBDHAL_ActivateEp(ep, false);
+
+  return USB_STATUS_OK;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set an endpoint0 in the stalled (halted) state.
+ *
+ * @details
+ *   Temporarily stalls endpoint 0. Used to signal a failure to respond to
+ *   the host's setup packet.
+ ******************************************************************************/
+void USBD_StallEp0()
+{
+    int const epAddr = 0;
+    USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
+    ep->in = true;
+    USBDHAL_StallEp( ep ); /* Stall Ep0 IN */
+    ep->in = false; /* OUT for next SETUP */
+    USBDHAL_StallEp( ep ); /* Stall Ep0 OUT */
+#if !defined( USB_DOEP0INT_STUPPKTRCVD )
+    USBDHAL_ReenableEp0Setup( dev ); /* Prepare for next SETUP pkt. */
+#else
+    USBDHAL_StartEp0Setup( dev );
+#endif
+    ep->state = D_EP_IDLE;
+}
+
+/******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************//**
+ * @{
+
+@page usb_device USB device stack library
+
+  The source files for the USB device stack resides in the usb directory
+  and follows the naming convention: em_usbd<em>nnn</em>.c/h.
+
+  @li @ref usb_device_intro
+  @li @ref usb_device_api
+  @li @ref usb_device_conf
+  @li @ref usb_device_powersave
+  @li @ref usb_device_example1
+
+
+@n @section usb_device_intro Introduction
+
+  The USB device protocol stack provides an API which makes it possible to
+  create USB devices with a minimum of effort. The device stack supports control,
+  bulk and interrupt transfers.
+
+  The stack is highly configurable to suit various needs, it does also contain
+  useful debugging features together with several demonstration projects to
+  get you started fast.
+
+  We recommend that you read through this documentation, then proceed to build
+  and test a few example projects before you start designing your own device.
+
+@n @section usb_device_api The device stack API
+
+  This section contains brief descriptions of the functions in the API. You will
+  find detailed information on input and output parameters and return values by
+  clicking on the hyperlinked function names. It is also a good idea to study
+  the code in the USB demonstration projects.
+
+  Your application code must include one header file: @em em_usb.h.
+
+  All functions defined in the API can be called from within interrupt handlers.
+
+  The USB stack use a hardware timer to keep track of time. TIMER0 is the
+  default choice, refer to @ref usb_device_conf for other possibilities.
+  Your application must not use the selected timer.
+
+  <b>Pitfalls:</b>@n
+    The USB peripheral will fill your receive buffers in quantities of WORD's
+    (4 bytes). Transmit and receive buffers must be WORD aligned, in
+    addition when allocating storage for receive buffers, round size up to
+    next WORD boundary. If it is possible that the host will send more data
+    than your device expects, round buffer size up to the next multiple of
+    maxpacket size for the relevant endpoint to avoid data corruption.
+
+    Transmit buffers passed to @htmlonly USBD_Write() @endhtmlonly must be
+    statically allocated because @htmlonly USBD_Write() @endhtmlonly only
+    initiates the transfer. When the host decide to actually perform the
+    transfer, your data must be available.
+
+  @n @ref USBD_Init() @n
+    This function is called to register your device and all its properties with
+    the device stack. The application must fill in a @ref USBD_Init_TypeDef
+    structure prior to calling. Refer to @ref DeviceInitCallbacks for the
+    optional callback functions defined within this structure. When this
+    function has been called your device is ready to be enumerated by the USB
+    host.
+
+  @ref USBD_Read(), @ref USBD_Write() @n
+    These functions initiate data transfers.
+    @n @htmlonly USBD_Read() @endhtmlonly initiate a transfer of data @em
+    from host @em to device (an @em OUT transfer in USB terminology).
+    @n @htmlonly USBD_Write() @endhtmlonly initiate a transfer of data @em from
+    device @em to host (an @em IN transfer).
+
+    When the USB host actually performs the transfer, your application will be
+    notified by means of a callback function which you provide (optionally).
+    Refer to @ref TransferCallback for details of the callback functionality.
+
+  @ref USBD_AbortTransfer(), @ref USBD_AbortAllTransfers() @n
+    These functions terminate transfers that are initiated, but has not yet
+    taken place. If a transfer is initiated with @htmlonly USBD_Read()
+    or USBD_Write(), @endhtmlonly but the USB host never actually peform
+    the transfers, these functions will deactivate the transfer setup to make
+    the USB device endpoint hardware ready for new (and potentially) different
+    transfers.
+
+  @ref USBD_Connect(), @ref USBD_Disconnect() @n
+    These functions turns the data-line (D+ or D-) pullup on or off. They can be
+    used to force reenumeration. It's good practice to delay at least one second
+    between @htmlonly USBD_Disconnect() and USBD_Connect() @endhtmlonly
+    to allow the USB host to unload the currently active device driver.
+
+  @ref USBD_EpIsBusy() @n
+    Check if an endpoint is busy.
+
+  @ref USBD_StallEp(), @ref USBD_UnStallEp() @n
+    These functions stalls or un-stalls an endpoint. This functionality may not
+    be needed by your application, but the USB device stack use them in response
+    to standard setup commands SET_FEATURE and CLEAR_FEATURE. They may be useful
+    when implementing some USB classes, e.g. a mass storage device use them
+    extensively.
+
+  @ref USBD_RemoteWakeup() @n
+    Used in SUSPENDED state (see @ref USB_Status_TypeDef) to signal resume to
+    host. It's the applications responsibility to adhere to the USB standard
+    which states that a device can not signal resume before it has been
+    SUSPENDED for at least 5 ms. The function will also check the configuration
+    descriptor defined by the application to see if it is legal for the device
+    to signal resume.
+
+  @ref USBD_GetUsbState() @n
+    Returns the device USB state (see @ref USBD_State_TypeDef). Refer to
+    Figure 9-1. "Device State Diagram" in the USB revision 2.0 specification.
+
+  @ref USBD_GetUsbStateName() @n
+    Returns a text string naming a given USB device state.
+
+  @ref USBD_SafeToEnterEM2() @n
+    Check if it is ok to enter energy mode EM2. Refer to the
+    @ref usb_device_powersave section for more information.
+
+  @n @anchor TransferCallback <b>The transfer complete callback function:</b> @n
+    @n USB_XferCompleteCb_TypeDef() is called when a transfer completes. It is
+    called with three parameters, the status of the transfer, the number of
+    bytes transferred and the number of bytes remaining. It may not always be
+    needed to have a callback on transfer completion, but you should keep in
+    mind that a transfer may be aborted when you least expect it. A transfer
+    will be aborted if host stalls the endpoint, if host resets your device, if
+    host unconfigures your device or if you unplug your device cable and the
+    device is selfpowered.
+    @htmlonly USB_XferCompleteCb_TypeDef() @endhtmlonly is also called if your
+    application use @htmlonly USBD_AbortTransfer() or USBD_AbortAllTransfers()
+    @endhtmlonly calls.
+    @note This callback is called from within an interrupt handler with
+          interrupts disabled.
+
+  @n @anchor DeviceInitCallbacks <b>Optional callbacks passed to the stack via
+    the @ref USBD_Init() function:</b> @n
+    @n These callbacks are all optional, and it is up to the application
+    programmer to decide if the application needs the functionality they
+    provide.
+    @note These callbacks are all called from within an interrupt handler
+          with interrupts disabled.
+
+  USBD_UsbResetCb_TypeDef() is called each time reset signalling is sensed on
+    the USB wire.
+
+  @n USBD_SofIntCb_TypeDef() is called with framenumber as a parameter on
+    each SOF interrupt.
+
+  @n USBD_DeviceStateChangeCb_TypeDef() is called whenever the device state
+    change. Useful for detecting e.g. SUSPENDED state change in order to reduce
+    current consumption of buspowered devices. The USB HID keyboard example
+    project has a good example on how to use this callback.
+
+  @n USBD_IsSelfPoweredCb_TypeDef() is called by the device stack when host
+    queries the device with a standard setup GET_STATUS command to check if the
+    device is currently selfpowered or buspowered. This feature is only
+    applicable on selfpowered devices which also works when only buspower is
+    available.
+
+  @n USBD_SetupCmdCb_TypeDef() is called each time a setup command is
+    received from host. Use this callback to override or extend the default
+    handling of standard setup commands, and to implement class or vendor
+    specific setup commands. The USB HID keyboard example project has a good
+    example on how to use this callback.
+
+  @n <b>Utility functions:</b> @n
+    @n    USB_PUTCHAR() Transmit a single char on the debug serial port.
+    @n @n USB_PUTS() Transmit a zero terminated string on the debug serial port.
+    @n @n USB_PRINTF() Transmit "printf" formated data on the debug serial port.
+    @n @n USB_GetErrorMsgString() Return an error message string for a given
+          error code.
+    @n @n USB_PrintErrorMsgString() Format and print a text string given an
+          error code, prepends an optional user supplied leader string.
+    @n @n USBTIMER_DelayMs() Active wait millisecond delay function. Can also be
+          used inside interrupt handlers.
+    @n @n USBTIMER_DelayUs() Active wait microsecond delay function. Can also be
+          used inside interrupt handlers.
+    @n @n USBTIMER_Init() Initialize the timer system. Called by @htmlonly
+          USBD_Init(), @endhtmlonly but your application must call it again to
+          reinitialize whenever you change the HFPERCLK frequency.
+    @n @n USBTIMER_Start() Start a timer. You can configure the USB device stack
+          to provide any number of timers. The timers have 1 ms resolution, your
+          application is notified of timeout by means of a callback.
+    @n @n USBTIMER_Stop() Stop a timer.
+
+@n @section usb_device_conf Configuring the device stack
+
+  Your application must provide a header file named @em usbconfig.h. This file
+  must contain the following \#define's:@n @n
+  @verbatim
+#define USB_DEVICE       // Compile the stack for device mode.
+#define NUM_EP_USED n    // Your application use 'n' endpoints in
+                         // addition to endpoint 0. @endverbatim
+
+  @n @em usbconfig.h may define the following items: @n @n
+  @verbatim
+#define NUM_APP_TIMERS n // Your application needs 'n' timers
+
+#define DEBUG_USB_API    // Turn on API debug diagnostics.
+
+// Some utility functions in the API needs printf. These
+// functions have "print" in their name. This macro enables
+// these functions.
+#define USB_USE_PRINTF   // Enable utility print functions.
+
+// Define a function for transmitting a single char on the serial port.
+extern int RETARGET_WriteChar(char c);
+#define USER_PUTCHAR  RETARGET_WriteChar
+
+#define USB_TIMER USB_TIMERn  // Select which hardware timer the USB stack
+                              // is allowed to use. Valid values are n=0,1,2...
+                              // corresponding to TIMER0, TIMER1, ...
+                              // If not specified, TIMER0 is used
+
+#define USB_VBUS_SWITCH_NOT_PRESENT  // Hardware does not have a VBUS switch
+
+#define USB_CORECLK_HFRCO   // Devices supporting crystal-less USB can use
+                            // HFRCO as core clock, default is HFXO
+@endverbatim
+
+  @n You are strongly encouraged to start application development with DEBUG_USB_API
+  turned on. When DEBUG_USB_API is turned on and USER_PUTCHAR is defined, useful
+  debugging information will be output on the development kit serial port.
+  Compiling with the DEBUG_EFM_USER flag will also enable all asserts
+  in both @em emlib and in the USB stack. If asserts are enabled and
+  USER_PUTCHAR defined, assert texts will be output on the serial port.
+
+  You application must include @em retargetserial.c if DEBUG_USB_API is defined
+  and @em retargetio.c if USB_USE_PRINTF is defined.
+  These files reside in the @em drivers
+  directory in the software package for your development board. Refer to
+  @ref usb_device_powersave for energy-saving mode configurations.
+
+@n @section usb_device_powersave Energy-saving modes
+
+  The device stack provides two energy saving levels. The first level is to
+  set the USB peripheral in energy saving mode, the next level is to enter
+  Energy Mode 2 (EM2). These energy saving modes can be applied when the device
+  is suspended by the USB host, or when when the device is not connected to a
+  USB host.
+  In addition to this an application can use energy modes EM1 and EM2. There
+  are no restrictions on when EM1 can be entered, EM2 can only be entered
+  when the USB device is suspended or detached from host.
+
+  Energy-saving modes are selected with a \#define in @em usbconfig.h, default
+  selection is to not use any energy saving modes.@n @n
+  @verbatim
+#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ENTEREM2)@endverbatim
+
+  There are three flags available, the flags can be or'ed together as shown above.
+
+  <b>\#define USB_PWRSAVE_MODE_ONSUSPEND</b>@n Set USB peripheral in low power
+  mode on suspend.
+
+  <b>\#define USB_PWRSAVE_MODE_ONVBUSOFF</b>@n Set USB peripheral in low power
+  mode when not attached to a host. This mode assumes that the internal voltage
+  regulator is used and that the VREGI pin of the chip is connected to VBUS.
+  This option can not be used with bus-powered devices.
+
+  <b>\#define USB_PWRSAVE_MODE_ENTEREM2</b>@n Enter EM2 when USB peripheral is
+  in low power mode.
+
+  When the USB peripheral is set in low power mode, it must be clocked by a 32kHz
+  clock. Both LFXO and LFRCO can be used, but only LFXO guarantee USB specification
+  compliance. Selection is done with a \#define in @em usbconfig.h.@n @n
+  @verbatim
+#define USB_USBC_32kHz_CLK   USB_USBC_32kHz_CLK_LFXO @endverbatim
+  Two flags are available, <b>USB_USBC_32kHz_CLK_LFXO</b> and
+  <b>USB_USBC_32kHz_CLK_LFRCO</b>. <b>USB_USBC_32kHz_CLK_LFXO</b> is selected
+  by default.
+
+  The USB HID keyboard and Mass Storage device example projects demonstrate
+  different energy-saving modes.
+
+  <b>Example 1:</b>
+  Leave all energy saving to the stack, the device enters EM2 on suspend and
+  when detached from host. @n
+  @verbatim
+In usbconfig.h:
+
+#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF | USB_PWRSAVE_MODE_ENTEREM2)
+  @endverbatim
+
+  @n <b>Example 2:</b>
+  Let the stack control energy saving in the USB periheral but let your
+  application control energy modes EM1 and EM2. @n
+  @verbatim
+In usbconfig.h:
+
+#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF)
+
+In application code:
+
+if ( USBD_SafeToEnterEM2() )
+  EMU_EnterEM2(true);
+else
+  EMU_EnterEM1(); @endverbatim
+
+@n @section usb_device_example1 Vendor unique device example application
+
+  This example represents the most simple USB device imaginable. It's purpose
+  is to turn user LED's on or off under control of vendor unique setup commands.
+  The device will rely on @em libusb device driver on the host, a host
+  application @em EFM32-LedApp.exe is bundled with the example.
+
+  The main() is really simple ! @n @n
+  @verbatim
+#include "em_usb.h"
+
+#include "descriptors.h"
+
+int main( void )
+{
+  BSP_Init(BSP_INIT_DEFAULT); // Initialize DK board register access
+  CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO );
+  BSP_LedsSet(0);             // Turn off all LED's
+
+  ConsoleDebugInit();         // Initialize UART for debug diagnostics
+
+  USB_PUTS( "\nEFM32 USB LED Vendor Unique Device example\n" );
+
+  USBD_Init( &initstruct );   // GO !
+
+  //When using a debugger it is pratical to uncomment the following three
+  //lines to force host to re-enumerate the device.
+
+  //USBD_Disconnect();
+  //USBTIMER_DelayMs( 1000 );
+  //USBD_Connect();
+
+  for (;;) {}
+} @endverbatim
+
+  @n Configure the device stack in <em>usbconfig.h</em>: @n @n
+  @verbatim
+#define USB_DEVICE                        // Compile stack for device mode.
+
+// **************************************************************************
+**                                                                         **
+** Specify number of endpoints used (in addition to EP0).                  **
+**                                                                         **
+*****************************************************************************
+#define NUM_EP_USED 0                     // EP0 is the only endpoint used.
+
+// **************************************************************************
+**                                                                         **
+** Configure serial port debug output.                                     **
+**                                                                         **
+*****************************************************************************
+// Prototype a function for transmitting a single char on the serial port.
+extern int RETARGET_WriteChar(char c);
+#define USER_PUTCHAR RETARGET_WriteChar
+
+// Enable debug diagnostics from API functions (illegal input params etc.)
+#define DEBUG_USB_API @endverbatim
+
+  @n Define device properties and fill in USB initstruct in
+  <em>descriptors.h</em>: @n @n
+  @verbatim
+EFM32_ALIGN(4)
+static const USB_DeviceDescriptor_TypeDef deviceDesc __attribute__ ((aligned(4))) =
+{
+  .bLength            = USB_DEVICE_DESCSIZE,
+  .bDescriptorType    = USB_DEVICE_DESCRIPTOR,
+  .bcdUSB             = 0x0200,
+  .bDeviceClass       = 0xFF,
+  .bDeviceSubClass    = 0,
+  .bDeviceProtocol    = 0,
+  .bMaxPacketSize0    = USB_FS_CTRL_EP_MAXSIZE,
+  .idVendor           = 0x10C4,
+  .idProduct          = 0x0001,
+  .bcdDevice          = 0x0000,
+  .iManufacturer      = 1,
+  .iProduct           = 2,
+  .iSerialNumber      = 3,
+  .bNumConfigurations = 1
+};
+
+EFM32_ALIGN(4)
+static const uint8_t configDesc[] __attribute__ ((aligned(4)))=
+{
+  // *** Configuration descriptor ***
+  USB_CONFIG_DESCSIZE,            // bLength
+  USB_CONFIG_DESCRIPTOR,          // bDescriptorType
+  USB_CONFIG_DESCSIZE +           // wTotalLength (LSB)
+  USB_INTERFACE_DESCSIZE,
+  (USB_CONFIG_DESCSIZE +          // wTotalLength (MSB)
+  USB_INTERFACE_DESCSIZE)>>8,
+  1,                              // bNumInterfaces
+  1,                              // bConfigurationValue
+  0,                              // iConfiguration
+  CONFIG_DESC_BM_RESERVED_D7 |    // bmAttrib: Self powered
+  CONFIG_DESC_BM_SELFPOWERED,
+  CONFIG_DESC_MAXPOWER_mA( 100 ), // bMaxPower: 100 mA
+
+  // *** Interface descriptor ***
+  USB_INTERFACE_DESCSIZE,         // bLength
+  USB_INTERFACE_DESCRIPTOR,       // bDescriptorType
+  0,                              // bInterfaceNumber
+  0,                              // bAlternateSetting
+  NUM_EP_USED,                    // bNumEndpoints
+  0xFF,                           // bInterfaceClass
+  0,                              // bInterfaceSubClass
+  0,                              // bInterfaceProtocol
+  0,                              // iInterface
+};
+
+STATIC_CONST_STRING_DESC_LANGID( langID, 0x04, 0x09 );
+STATIC_CONST_STRING_DESC( iManufacturer, 'E','n','e','r','g','y',' ',       \
+                                         'M','i','c','r','o',' ','A','S' );
+STATIC_CONST_STRING_DESC( iProduct     , 'V','e','n','d','o','r',' ',       \
+                                         'U','n','i','q','u','e',' ',       \
+                                         'L','E','D',' ',                   \
+                                         'D','e','v','i','c','e' );
+STATIC_CONST_STRING_DESC( iSerialNumber, '0','0','0','0','0','0',           \
+                                         '0','0','1','2','3','4' );
+
+static const void * const strings[] =
+{
+  &langID,
+  &iManufacturer,
+  &iProduct,
+  &iSerialNumber
+};
+
+// Endpoint buffer sizes
+// 1 = single buffer, 2 = double buffering, 3 = tripple buffering ...
+static const uint8_t bufferingMultiplier[ NUM_EP_USED + 1 ] = { 1 };
+
+static const USBD_Callbacks_TypeDef callbacks =
+{
+  .usbReset        = NULL,
+  .usbStateChange  = NULL,
+  .setupCmd        = SetupCmd,
+  .isSelfPowered   = NULL,
+  .sofInt          = NULL
+};
+
+static const USBD_Init_TypeDef initstruct =
+{
+  .deviceDescriptor    = &deviceDesc,
+  .configDescriptor    = configDesc,
+  .stringDescriptors   = strings,
+  .numberOfStrings     = sizeof(strings)/sizeof(void*),
+  .callbacks           = &callbacks,
+  .bufferingMultiplier = bufferingMultiplier
+};  @endverbatim
+
+  @n Now we have to implement vendor unique USB setup commands to control the
+  LED's (see callbacks variable above). Notice that the buffer variable below is
+  statically allocated because @htmlonly USBD_Write() @endhtmlonly only
+  initiates the transfer. When the host actually performs the transfer, the
+  SetupCmd() function will have returned ! @n @n
+
+  @verbatim
+#define VND_GET_LEDS 0x10
+#define VND_SET_LED  0x11
+
+static int SetupCmd( const USB_Setup_TypeDef *setup )
+{
+  int retVal;
+  uint16_t leds;
+  static uint32_t buffer;
+  uint8_t *pBuffer = (uint8_t*)&buffer;
+
+  retVal = USB_STATUS_REQ_UNHANDLED;
+
+  if ( setup->Type == USB_SETUP_TYPE_VENDOR )
+  {
+    switch ( setup->bRequest )
+    {
+      case VND_GET_LEDS:
+      // ********************
+        *pBuffer = BSP_LedsGet() & 0x1F;
+        retVal = USBD_Write( 0, pBuffer, setup->wLength, NULL );
+        break;
+
+      case VND_SET_LED:
+      // ********************
+        leds = DVK_getLEDs() & 0x1F;
+        if ( setup->wValue )
+        {
+          leds |= LED0 << setup->wIndex;
+        }
+        else
+        {
+          leds &= ~( LED0 << setup->wIndex );
+        }
+        BSP_LedsSet( leds );
+        retVal = USB_STATUS_OK;
+        break;
+    }
+  }
+
+  return retVal;
+}@endverbatim
+
+ * @}**************************************************************************/
+
+#endif /* defined( USB_DEVICE ) */
+#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */