2018-09-12 20:00:13 -04:00

756 lines
17 KiB
C

/**************************************************************************//**
* Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved.
*
* http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt
*****************************************************************************/
#include "si_toolchain.h"
#include "efm8_usb.h"
//#include "assert.h"
#include <stdint.h>
#define SLAB_ASSERT(x)
// -----------------------------------------------------------------------------
// Global Variables
/// Tracks the state of the USB device and endpoints and contains pointers
/// to all descriptors.
SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG);
// -----------------------------------------------------------------------------
// Macros
/// Returns the requested endpoint object of type USBD_Ep_TypeDef
/// This macro does not check that epAddr is valid, so the calling function
/// should verify epAddr before using the macro.
#define GetEp(epAddr) (&myUsbDevice.ep0 + epAddr)
#if SLAB_USB_POLLED_MODE
#define DISABLE_USB_INTS {}
#define ENABLE_USB_INTS {}
#else
/// Saves the current state of the USB Interrupt Enable to a variable called
/// usbIntsEnabled, then disables USB interrupts.
#define DISABLE_USB_INTS { usbIntsEnabled = USB_GetIntsEnabled(); USB_DisableInts(); }
/// Sets the USB Interrupt Enable bit to the value of usbIntsEnabled.
/// @ref DISABLE_USB_INTS must be used before this macro is used.
#define ENABLE_USB_INTS { if (usbIntsEnabled) {USB_EnableInts(); } }
#endif // SLAB_USB_POLLED_MODE
// Function in efm8_usbdint.c to force load the module for libraries
extern void forceModuleLoad_usbint(void);
// -----------------------------------------------------------------------------
// USB API Functions
void USBD_AbortAllTransfers(void)
{
uint8_t i;
bool usbIntsEnabled;
USB_SaveSfrPage();
DISABLE_USB_INTS;
// Call USBD_AbortTransfer() for each endpoint
for (i = 1; i < SLAB_USB_NUM_EPS_USED; i++)
{
USBD_AbortTransfer(i);
}
ENABLE_USB_INTS;
USB_RestoreSfrPage();
}
int8_t USBD_AbortTransfer(uint8_t epAddr)
{
USBD_Ep_TypeDef MEM_MODEL_SEG *ep;
uint8_t retVal = USB_STATUS_OK;
bool usbIntsEnabled;
USB_SaveSfrPage();
// Verify this is a valid endpoint address and is not Endpoint 0.
if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED))
{
SLAB_ASSERT(false);
retVal = USB_STATUS_ILLEGAL;
}
else
{
DISABLE_USB_INTS;
ep = GetEp(epAddr);
// If the state of the endpoint is already idle, there is not need to abort
// a transfer
if (ep->state != D_EP_IDLE)
{
switch (epAddr)
{
#if SLAB_USB_EP1IN_USED
case EP1IN:
USB_AbortInEp(1);
break;
#endif
#if SLAB_USB_EP2IN_USED
case EP2IN:
USB_AbortInEp(2);
break;
#endif
#if SLAB_USB_EP3IN_USED
case EP3IN:
USB_AbortInEp(3);
break;
#endif
#if SLAB_USB_EP1OUT_USED
case EP1OUT:
USB_AbortOutEp(1);
break;
#endif
#if SLAB_USB_EP2OUT_USED
case EP2OUT:
USB_AbortOutEp(2);
break;
#endif
#if SLAB_USB_EP3OUT_USED
case EP3OUT:
USB_AbortOutEp(3);
break;
#endif
}
// Set the endpoint state to idle and clear out endpoint state variables
ep->state = D_EP_IDLE;
ep->misc.c = 0;
}
}
ENABLE_USB_INTS;
USB_RestoreSfrPage();
return retVal;
}
void USBD_Connect(void)
{
USB_SaveSfrPage();
myUsbDevice.ep0.state = D_EP_IDLE;
USB_EnablePullUpResistor();
USB_EnableTransceiver();
USB_RestoreSfrPage();
}
void USBD_Disconnect(void)
{
USB_SaveSfrPage();
USB_DisablePullUpResistor();
USB_RestoreSfrPage();
}
bool USBD_EpIsBusy(uint8_t epAddr)
{
USBD_Ep_TypeDef MEM_MODEL_SEG *ep;
// Verify this is a valid endpoint address
if (epAddr >= SLAB_USB_NUM_EPS_USED)
{
SLAB_ASSERT(false);
return true;
}
ep = GetEp(epAddr);
if (ep->state == D_EP_IDLE)
{
return false;
}
return true;
}
USBD_State_TypeDef USBD_GetUsbState(void)
{
return myUsbDevice.state;
}
int8_t USBD_Init(const USBD_Init_TypeDef *p)
{
uint8_t i;
USB_SaveSfrPage();
USB_DisableInts();
// This forces the liner to bring in the contents efm8_usbdint
// It is place here since all users MUST call this function
// for the library to work properly
forceModuleLoad_usbint();
// Zero out the myUsbDevice struct, then initialize all non-zero members
for (i = 0; i < sizeof(myUsbDevice); i++)
{
*((uint8_t MEM_MODEL_SEG *)&myUsbDevice + i) = 0;
}
// Get the USB descriptors from p
myUsbDevice.deviceDescriptor = p->deviceDescriptor;
myUsbDevice.configDescriptor = (USB_ConfigurationDescriptor_TypeDef *)p->configDescriptor;
myUsbDevice.stringDescriptors = p->stringDescriptors;
myUsbDevice.numberOfStrings = p->numberOfStrings;
// Enable USB clock
#if SLAB_USB_FULL_SPEED
USB_SetClockIntOsc();
USB_SelectFullSpeed();
#else
USB_SetClockIntOscDiv8();
USB_SelectLowSpeed();
#endif // SLAB_USB_FULL_SPEED
// Enable or disable VBUS detection
#if SLAB_USB_BUS_POWERED
USB_VbusDetectDisable();
#else
USB_VbusDetectEnable();
#endif
USB_ForceReset();
USB_EnableDeviceInts();
USBD_Connect();
// If VBUS is present, the state should be Default.
// Otherwise, it is Attached.
#if SLAB_USB_BUS_POWERED
myUsbDevice.state = USBD_STATE_DEFAULT;
#else
if (USB_IsVbusOn())
{
myUsbDevice.state = USBD_STATE_DEFAULT;
}
else
{
myUsbDevice.state = USBD_STATE_ATTACHED;
}
#endif
// Only enable USB interrupts when not in polled mode
#if (SLAB_USB_POLLED_MODE == 0)
USB_EnableInts();
#endif
USB_RestoreSfrPage();
USB_DisableInhibit();
return USB_STATUS_OK;
}
int8_t USBD_Read(uint8_t epAddr,
uint8_t *dat,
uint16_t byteCount,
bool callback)
{
bool usbIntsEnabled;
USBD_Ep_TypeDef MEM_MODEL_SEG *ep;
USB_SaveSfrPage();
// Verify the endpoint address is valid.
switch (epAddr)
{
case EP0:
#if SLAB_USB_EP1OUT_USED
case EP1OUT:
#endif
#if SLAB_USB_EP2OUT_USED
case EP2OUT:
#endif
#if SLAB_USB_EP3OUT_USED
case EP3OUT:
#endif
break;
#if SLAB_USB_EP1IN_USED
case EP1IN:
#endif
#if SLAB_USB_EP2IN_USED
case EP2IN:
#endif
#if SLAB_USB_EP3IN_USED
case EP3IN:
#endif
default:
SLAB_ASSERT(false);
return USB_STATUS_ILLEGAL;
}
// If the device has not been configured, we cannot start a transfer.
if ((epAddr != EP0) && (myUsbDevice.state != USBD_STATE_CONFIGURED))
{
return USB_STATUS_DEVICE_UNCONFIGURED;
}
ep = GetEp(epAddr);
// If the endpoint is not idle, we cannot start a new transfer.
// Return the appropriate error code.
if (ep->state != D_EP_IDLE)
{
if (ep->state == D_EP_STALL)
{
return USB_STATUS_EP_STALLED;
}
else
{
return USB_STATUS_EP_BUSY;
}
}
DISABLE_USB_INTS;
ep->buf = dat;
ep->remaining = byteCount;
ep->state = D_EP_RECEIVING;
ep->misc.bits.callback = callback;
ep->misc.bits.waitForRead = false;
// If isochronous, set the buffer index to 0
#if ((SLAB_USB_EP3OUT_USED) && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC))
if (epAddr == EP3OUT)
{
myUsbDevice.ep3outIsoIdx = 0;
}
#endif
ENABLE_USB_INTS;
USB_RestoreSfrPage();
return USB_STATUS_OK;
}
#if SLAB_USB_REMOTE_WAKEUP_ENABLED
int8_t USBD_RemoteWakeup(void)
{
// The device must be suspended and Remote Wakeup must have been previously
// configured with a SET_FEATURE (Remote Wakeup) command.
if ((myUsbDevice.state != USBD_STATE_SUSPENDED) ||
(myUsbDevice.remoteWakeupEnabled == false))
{
return USB_STATUS_ILLEGAL;
}
USB_ForceResume();
USBD_RemoteWakeupDelay(); // Application will provide the delay between
// starting and stopping the resume signal.
USB_ClearResume();
return USB_STATUS_OK;
}
#endif // SLAB_USB_REMOTE_WAKEUP_ENABLED
#if SLAB_USB_POLLED_MODE
void USBD_Run(void)
{
usbIrqHandler();
}
#endif // SLAB_USB_POLLED_MODE
int8_t USBD_StallEp(uint8_t epAddr)
{
bool usbIntsEnabled;
USB_SaveSfrPage();
// Verify the endpoint address is valid and not Endpoint 0.
if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED))
{
SLAB_ASSERT(false);
return USB_STATUS_ILLEGAL;
}
DISABLE_USB_INTS;
// Halt the appropriate endpoint by sending a stall and setting the endpoint
// state to Halted (D_EP_HALT).
switch (epAddr)
{
#if SLAB_USB_EP1IN_USED
case (EP1IN):
myUsbDevice.ep1in.state = D_EP_HALT;
USB_SetIndex(1);
USB_EpnInStall();
break;
#endif
#if SLAB_USB_EP2IN_USED
case (EP2IN):
myUsbDevice.ep2in.state = D_EP_HALT;
USB_SetIndex(2);
USB_EpnInStall();
break;
#endif
#if SLAB_USB_EP3IN_USED
case (EP3IN):
myUsbDevice.ep3in.state = D_EP_HALT;
USB_SetIndex(3);
USB_EpnInStall();
break;
#endif
#if SLAB_USB_EP1OUT_USED
case (EP1OUT):
myUsbDevice.ep1out.state = D_EP_HALT;
USB_SetIndex(1);
USB_EpnOutStall();
break;
#endif
#if SLAB_USB_EP2OUT_USED
case (EP2OUT):
myUsbDevice.ep2out.state = D_EP_HALT;
USB_SetIndex(2);
USB_EpnOutStall();
break;
#endif
#if SLAB_USB_EP3OUT_USED
case (EP3OUT):
myUsbDevice.ep3out.state = D_EP_HALT;
USB_SetIndex(3);
USB_EpnOutStall();
break;
#endif
}
ENABLE_USB_INTS;
USB_RestoreSfrPage();
return USB_STATUS_OK;
}
void USBD_Stop(void)
{
USB_DisableInts();
USBD_Disconnect();
USBD_SetUsbState(USBD_STATE_NONE);
}
void USBD_Suspend(void)
{
uint8_t i;
bool regulatorEnabled, prefetchEnabled;
USB_SaveSfrPage();
// If the USB_PWRSAVE_MODE_ONVBUSOFF is enabled, we can enter suspend if VBUS
// is not present even if the USB has not detected a suspend event.
#if ((!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) || \
(SLAB_USB_BUS_POWERED))
if (USB_IsSuspended() == true)
#else
if ((USB_IsSuspended() == true) || (USB_IsVbusOn() == false))
#endif
{
USB_SuspendTransceiver();
#if SLAB_USB_FULL_SPEED
USB_SetSuspendClock();
#endif
// Get the state of the prefetch engine enable bit and disable the prefetch
// engine
prefetchEnabled = USB_IsPrefetchEnabled();
USB_DisablePrefetch();
// Get the state of the internal regulator before suspending it.
if (USB_IsRegulatorEnabled() == true)
{
regulatorEnabled = true;
#if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_FASTWAKE)
USB_SuspendRegulatorFastWake();
#else
USB_SuspendRegulator();
// Wait at least 12 clock instructions before halting the internal oscillator
for (i = 0; i < 3; i++)
{
}
#endif
}
else
{
regulatorEnabled = false;
}
do
{
USB_SuspendOscillator();
// When we arrive here, the device has waked from suspend mode.
#if SLAB_USB_REMOTE_WAKEUP_ENABLED
// If remote wakeup is enabled, query the application if the remote
// wakeup event occurred. If so, exit USBD_Suspend().
if (USB_IsSuspended() == true)
{
if (USBD_RemoteWakeupCb() == true)
{
break;
}
}
#endif
#if ((!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) && \
(SLAB_USB_BUS_POWERED == 0))
// If the USB_PWRSAVE_MODE_ONVBUSOFF mode is disabled, VBUS has been
// removed, so exit USBD_Suspend().
if (USB_IsVbusOn() == false)
{
break;
}
#endif
} while (USB_IsSuspended() == true);
// Restore the internal regulator
if (regulatorEnabled == true)
{
USB_UnsuspendRegulator();
}
// Restore the prefetch engine
if (prefetchEnabled == true)
{
USB_EnablePrefetch();
}
#if SLAB_USB_FULL_SPEED
// Restore the clock
USB_SetNormalClock();
#endif
USB_EnableTransceiver();
}
USB_RestoreSfrPage();
}
int8_t USBD_UnStallEp(uint8_t epAddr)
{
bool usbIntsEnabled;
USB_SaveSfrPage();
// Verify the endpoint address is valid and not Endpoint 0.
if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED))
{
SLAB_ASSERT(false);
return USB_STATUS_ILLEGAL;
}
else
{
DISABLE_USB_INTS;
// End the stall condition and set the endpoint state to idle.
switch (epAddr)
{
#if SLAB_USB_EP1IN_USED
case (EP1IN):
myUsbDevice.ep1in.state = D_EP_IDLE;
USB_SetIndex(1);
USB_EpnInEndStall();
break;
#endif
#if SLAB_USB_EP2IN_USED
case (EP2IN):
myUsbDevice.ep2in.state = D_EP_IDLE;
USB_SetIndex(2);
USB_EpnInEndStall();
break;
#endif
#if SLAB_USB_EP3IN_USED
case (EP3IN):
myUsbDevice.ep3in.state = D_EP_IDLE;
USB_SetIndex(3);
USB_EpnInEndStall();
break;
#endif
#if SLAB_USB_EP1OUT_USED
case (EP1OUT):
myUsbDevice.ep1out.state = D_EP_IDLE;
USB_SetIndex(1);
USB_EpnOutEndStall();
break;
#endif
#if SLAB_USB_EP2OUT_USED
case (EP2OUT):
myUsbDevice.ep2out.state = D_EP_IDLE;
USB_SetIndex(2);
USB_EpnOutEndStall();
break;
#endif
#if SLAB_USB_EP3OUT_USED
case (EP3OUT):
myUsbDevice.ep3out.state = D_EP_IDLE;
USB_SetIndex(3);
USB_EpnOutEndStall();
break;
#endif
}
ENABLE_USB_INTS;
USB_RestoreSfrPage();
}
return USB_STATUS_OK;
}
int8_t USBD_Write(uint8_t epAddr,
uint8_t *dat,
uint16_t byteCount,
bool callback)
{
bool usbIntsEnabled;
USBD_Ep_TypeDef MEM_MODEL_SEG *ep;
USB_SaveSfrPage();
// Verify the endpoint address is valid.
switch (epAddr)
{
case EP0:
#if SLAB_USB_EP1IN_USED
case EP1IN:
#endif
#if SLAB_USB_EP2IN_USED
case EP2IN:
#endif
#if SLAB_USB_EP3IN_USED
case EP3IN:
#endif
break;
#if SLAB_USB_EP1OUT_USED
case EP1OUT:
#endif
#if SLAB_USB_EP2OUT_USED
case EP2OUT:
#endif
#if SLAB_USB_EP3OUT_USED
case EP3OUT:
#endif
default:
SLAB_ASSERT(false);
return USB_STATUS_ILLEGAL;
}
// If the device is not configured and it is not Endpoint 0, we cannot begin
// a transfer.
if ((epAddr != EP0) && (myUsbDevice.state != USBD_STATE_CONFIGURED))
{
return USB_STATUS_DEVICE_UNCONFIGURED;
}
ep = GetEp(epAddr);
// If the endpoint is not idle, we cannot start a new transfer.
// Return the appropriate error code.
if (ep->state != D_EP_IDLE)
{
if (ep->state == D_EP_STALL)
{
return USB_STATUS_EP_STALLED;
}
else
{
return USB_STATUS_EP_BUSY;
}
}
DISABLE_USB_INTS;
ep->buf = dat;
ep->remaining = byteCount;
ep->state = D_EP_TRANSMITTING;
ep->misc.bits.callback = callback;
switch (epAddr)
{
// For Endpoint 0, set the inPacketPending flag to true. The USB handler
// will see this on the next SOF and begin the transfer.
case (EP0):
myUsbDevice.ep0.misc.bits.inPacketPending = true;
break;
// For data endpoints, we will call USB_WriteFIFO here to reduce latency
// between the call to USBD_Write() and the first packet being sent.
#if SLAB_USB_EP1IN_USED
case (EP1IN):
USB_WriteFIFO(1,
(byteCount > SLAB_USB_EP1IN_MAX_PACKET_SIZE) ? SLAB_USB_EP1IN_MAX_PACKET_SIZE : byteCount,
myUsbDevice.ep1in.buf,
true);
break;
#endif // SLAB_USB_EP1IN_USED
#if SLAB_USB_EP2IN_USED
case (EP2IN):
USB_WriteFIFO(2,
(byteCount > SLAB_USB_EP2IN_MAX_PACKET_SIZE) ? SLAB_USB_EP2IN_MAX_PACKET_SIZE : byteCount,
myUsbDevice.ep2in.buf,
true);
break;
#endif // SLAB_USB_EP2IN_USED
#if SLAB_USB_EP3IN_USED
case (EP3IN):
#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR))
USB_WriteFIFO(3,
(byteCount > SLAB_USB_EP3IN_MAX_PACKET_SIZE) ? SLAB_USB_EP3IN_MAX_PACKET_SIZE : byteCount,
myUsbDevice.ep3in.buf,
true);
#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)
myUsbDevice.ep3in.misc.bits.inPacketPending = true;
myUsbDevice.ep3inIsoIdx = 0;
#endif
break;
#endif // SLAB_USB_EP3IN_USED
}
ENABLE_USB_INTS;
USB_RestoreSfrPage();
return USB_STATUS_OK;
}
// -----------------------------------------------------------------------------
// UtilityFunctions
void USBD_SetUsbState(USBD_State_TypeDef newState)
{
#if (SLAB_USB_SUPPORT_ALT_INTERFACES)
uint8_t i;
#endif
USBD_State_TypeDef currentState;
currentState = myUsbDevice.state;
// If the device is un-configuring, disable the data endpoints and clear out
// alternate interface settings
if ((currentState >= USBD_STATE_SUSPENDED)
&& (newState < USBD_STATE_SUSPENDED))
{
USBD_AbortAllTransfers();
#if (SLAB_USB_SUPPORT_ALT_INTERFACES)
for (i = 0; i < SLAB_USB_NUM_INTERFACES; i++)
{
myUsbDevice.interfaceAltSetting[i] = 0;
}
#endif
}
if (newState == USBD_STATE_SUSPENDED)
{
myUsbDevice.savedState = currentState;
}
myUsbDevice.state = newState;
#if SLAB_USB_STATE_CHANGE_CB
if (currentState != newState)
{
USBD_DeviceStateChangeCb(currentState, newState);
}
#endif
}