solo/targets/efm8/lib/efm8_usb/src/efm8_usbdint.c
2018-09-12 20:00:13 -04:00

534 lines
16 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 <stdint.h>
#include <endian.h>
// -----------------------------------------------------------------------------
// Global variables
extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG);
extern SI_SEGMENT_VARIABLE(txZero[2], uint8_t, SI_SEG_CODE);
// -----------------------------------------------------------------------------
// Function prototypes
static void handleUsbEp0Int(void);
static void handleUsbResetInt(void);
static void handleUsbSuspendInt(void);
static void handleUsbResumeInt(void);
static void handleUsbEp0Tx(void);
static void handleUsbEp0Rx(void);
static void USB_ReadFIFOSetup(void);
#if (SLAB_USB_EP1IN_USED)
void handleUsbIn1Int(void);
#endif // SLAB_USB_EP1IN_USED
#if (SLAB_USB_EP2IN_USED)
void handleUsbIn2Int(void);
#endif // SLAB_USB_EP2IN_USED
#if (SLAB_USB_EP3IN_USED)
void handleUsbIn3Int(void);
#endif // SLAB_USB_EP3IN_USED
#if (SLAB_USB_EP1OUT_USED)
void handleUsbOut1Int(void);
#endif // SLAB_USB_EP1OUT_USED
#if (SLAB_USB_EP2OUT_USED)
void handleUsbOut2Int(void);
#endif // SLAB_USB_EP2OUT_USED
#if (SLAB_USB_EP3OUT_USED)
void handleUsbOut3Int(void);
#endif // SLAB_USB_EP3OUT_USED
void SendEp0Stall(void);
// -----------------------------------------------------------------------------
// Functions
/***************************************************************************//**
* @brief First-level handler for USB peripheral interrupt
* @details If @ref SLAB_USB_POLLED_MODE is 1, this becomes a regular
* function instead of an ISR and must be called by the application
* periodically.
******************************************************************************/
#if (SLAB_USB_POLLED_MODE == 0)
SI_INTERRUPT(usbIrqHandler, USB0_IRQn)
#else
void usbIrqHandler(void)
#endif
{
uint8_t statusCommon, statusIn, statusOut, indexSave;
#if SLAB_USB_HANDLER_CB
// Callback to user before processing
USBD_EnterHandler();
#endif
// Get the interrupt sources
statusCommon = USB_GetCommonInts();
statusIn = USB_GetInInts();
statusOut = USB_GetOutInts();
#if SLAB_USB_POLLED_MODE
if ((statusCommon == 0) && (statusIn == 0) && (statusOut == 0))
{
return;
}
#endif
// Save the current index
indexSave = USB_GetIndex();
// Check Common USB Interrupts
if (USB_IsSofIntActive(statusCommon))
{
#if SLAB_USB_SOF_CB
USBD_SofCb(USB_GetSofNumber());
#endif // SLAB_USB_SOF_CB
// Check for unhandled USB packets on EP0 and set the corresponding IN or
// OUT interrupt active flag if necessary.
if (((myUsbDevice.ep0.misc.bits.outPacketPending == true) && (myUsbDevice.ep0.state == D_EP_RECEIVING)) ||
((myUsbDevice.ep0.misc.bits.inPacketPending == true) && (myUsbDevice.ep0.state == D_EP_TRANSMITTING)))
{
USB_SetEp0IntActive(statusIn);
}
// Check for unhandled USB OUT packets and set the corresponding OUT
// interrupt active flag if necessary.
#if SLAB_USB_EP1OUT_USED
if ((myUsbDevice.ep1out.misc.bits.outPacketPending == true) && (myUsbDevice.ep1out.state == D_EP_RECEIVING))
{
USB_SetOut1IntActive(statusOut);
}
#endif
#if SLAB_USB_EP2OUT_USED
if ((myUsbDevice.ep2out.misc.bits.outPacketPending == true) && (myUsbDevice.ep2out.state == D_EP_RECEIVING))
{
USB_SetOut2IntActive(statusOut);
}
#endif
#if SLAB_USB_EP3OUT_USED
if ((myUsbDevice.ep3out.misc.bits.outPacketPending == true) && (myUsbDevice.ep3out.state == D_EP_RECEIVING))
{
USB_SetOut3IntActive(statusOut);
}
#endif
#if (SLAB_USB_EP3IN_USED && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC))
if ((myUsbDevice.ep3in.misc.bits.inPacketPending == true) && (myUsbDevice.ep3in.state == D_EP_TRANSMITTING))
{
USB_SetIn3IntActive(statusIn);
}
#endif
}
if (USB_IsResetIntActive(statusCommon))
{
handleUsbResetInt();
// If VBUS is not present on detection of a USB reset, enter suspend mode.
#if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)
if (USB_IsVbusOn() == false)
{
USB_SetSuspendIntActive(statusCommon);
}
#endif
}
if (USB_IsResumeIntActive(statusCommon))
{
handleUsbResumeInt();
}
if (USB_IsSuspendIntActive(statusCommon))
{
handleUsbSuspendInt();
}
#if SLAB_USB_EP3IN_USED
if (USB_IsIn3IntActive(statusIn))
{
handleUsbIn3Int();
}
#endif // EP3IN_USED
#if SLAB_USB_EP3OUT_USED
if (USB_IsOut3IntActive(statusOut))
{
handleUsbOut3Int();
}
#endif // EP3OUT_USED
#if SLAB_USB_EP2IN_USED
if (USB_IsIn2IntActive(statusIn))
{
handleUsbIn2Int();
}
#endif // EP2IN_USED
#if SLAB_USB_EP1IN_USED
if (USB_IsIn1IntActive(statusIn))
{
handleUsbIn1Int();
}
#endif // EP1IN_USED
#if SLAB_USB_EP2OUT_USED
if (USB_IsOut2IntActive(statusOut))
{
handleUsbOut2Int();
}
#endif // EP2OUT_USED
#if SLAB_USB_EP1OUT_USED
if (USB_IsOut1IntActive(statusOut))
{
handleUsbOut1Int();
}
#endif // EP1OUT_USED
// Check USB Endpoint 0 Interrupt
if (USB_IsEp0IntActive(statusIn))
{
handleUsbEp0Int();
}
// Restore index
USB_SetIndex(indexSave);
#if SLAB_USB_HANDLER_CB
// Callback to user before exiting
USBD_ExitHandler();
#endif
}
/***************************************************************************//**
* @brief Handles Endpoint 0 transfer interrupt
******************************************************************************/
static void handleUsbEp0Int(void)
{
USB_SetIndex(0);
if (USB_Ep0SentStall() || USB_GetSetupEnd())
{
USB_Ep0ClearSentStall();
USB_ServicedSetupEnd();
myUsbDevice.ep0.state = D_EP_IDLE;
myUsbDevice.ep0.misc.c = 0;
}
if (USB_Ep0OutPacketReady())
{
if (myUsbDevice.ep0.misc.bits.waitForRead == true)
{
myUsbDevice.ep0.misc.bits.outPacketPending = true;
}
else if (myUsbDevice.ep0.state == D_EP_IDLE)
{
myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE;
USB_ReadFIFOSetup();
// Vendor unique, Class or Standard setup commands override?
#if SLAB_USB_SETUP_CMD_CB
if (USBD_SetupCmdCb(&myUsbDevice.setup) == USB_STATUS_REQ_UNHANDLED)
{
#endif
if (myUsbDevice.setup.bmRequestType.Type == USB_SETUP_TYPE_STANDARD)
{
USBDCH9_SetupCmd();
}
else
{
SendEp0Stall();
}
#if SLAB_USB_SETUP_CMD_CB
}
else
{
// If in-packet but callback didn't setup a USBD_Read and we are expecting a data byte then
// we need to wait for the read to be setup and nack packets till USBD_Read is called.
if ((myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_OUT)
&& (myUsbDevice.ep0.state != D_EP_RECEIVING)
&& (myUsbDevice.setup.wLength)
)
{
myUsbDevice.ep0.misc.bits.waitForRead = true;
}
}
#endif
}
else if (myUsbDevice.ep0.state == D_EP_RECEIVING)
{
handleUsbEp0Rx();
}
else
{
myUsbDevice.ep0.misc.bits.outPacketPending = true;
}
}
if ((myUsbDevice.ep0.state == D_EP_TRANSMITTING) && (USB_Ep0InPacketReady() == 0))
{
handleUsbEp0Tx();
}
}
/***************************************************************************//**
* @brief Reads and formats a setup packet
******************************************************************************/
static void USB_ReadFIFOSetup(void)
{
uint16_t MEM_MODEL_SEG *ptr = &myUsbDevice.setup;
USB_ReadFIFO(0, 8, (uint8_t *)ptr);
USB_Ep0ServicedOutPacketReady();
// Modify for Endian-ness of the compiler
ptr[1] = le16toh(ptr[1]);
ptr[2] = le16toh(ptr[2]);
ptr[3] = le16toh(ptr[3]);
}
/***************************************************************************//**
* @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 (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
}
/***************************************************************************//**
* @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
}
}
/***************************************************************************//**
* @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 transmit data phase on Endpoint 0
******************************************************************************/
static void handleUsbEp0Tx(void)
{
uint8_t count, count_snapshot, i;
bool callback = myUsbDevice.ep0.misc.bits.callback;
// The number of bytes to send in the next packet must be less than or equal
// to the maximum EP0 packet size.
count = (myUsbDevice.ep0.remaining >= USB_EP0_SIZE) ?
USB_EP0_SIZE : myUsbDevice.ep0.remaining;
// Save the packet size for future use.
count_snapshot = count;
// Strings can use the USB_STRING_DESCRIPTOR_UTF16LE_PACKED type to pack
// UTF16LE data without the zero's between each character.
// If the current string is of type USB_STRING_DESCRIPTOR_UTF16LE_PACKED,
// unpacket it by inserting a zero between each character in the string.
if (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF16LE_PACKED)
{
// If ep0String.encoding.init is true, this is the beginning of the string.
// The first two bytes of the string are the bLength and bDescriptorType
// fields. These are no packed like the reset of the string, so write them
// to the FIFO and set ep0String.encoding.init to false.
if (myUsbDevice.ep0String.encoding.init == true)
{
USB_WriteFIFO(0, 2, myUsbDevice.ep0.buf, false);
myUsbDevice.ep0.buf += 2;
count -= 2;
myUsbDevice.ep0String.encoding.init = false;
}
// Insert a 0x00 between each character of the string.
for (i = 0; i < count / 2; i++)
{
USB_WriteFIFO(0, 1, myUsbDevice.ep0.buf, false);
myUsbDevice.ep0.buf++;
USB_WriteFIFO(0, 1, &txZero, false);
}
}
// For any data other than USB_STRING_DESCRIPTOR_UTF16LE_PACKED, just send the
// data normally.
else
{
USB_WriteFIFO(0, count, myUsbDevice.ep0.buf, false);
myUsbDevice.ep0.buf += count;
}
myUsbDevice.ep0.misc.bits.inPacketPending = false;
myUsbDevice.ep0.remaining -= count_snapshot;
// If the last packet of the transfer is exactly the maximum EP0 packet size,
// we will have to send a ZLP (zero-length packet) after the last data packet
// to signal to the host that the transfer is complete.
// Check for the ZLP packet case here.
if ((myUsbDevice.ep0.remaining == 0) && (count_snapshot != USB_EP0_SIZE))
{
USB_Ep0SetLastInPacketReady();
myUsbDevice.ep0.state = D_EP_IDLE;
myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE;
myUsbDevice.ep0.misc.c = 0;
}
else
{
// Do not call USB_Ep0SetLastInPacketReady() because we still need to send
// the ZLP.
USB_Ep0SetInPacketReady();
}
// Make callback if requested
if (callback == true)
{
USBD_XferCompleteCb(EP0, USB_STATUS_OK, count_snapshot, myUsbDevice.ep0.remaining);
}
}
/***************************************************************************//**
* @brief Handles receive data phase on Endpoint 0
******************************************************************************/
void handleUsbEp0Rx(void)
{
uint8_t count;
USB_Status_TypeDef status;
bool callback = myUsbDevice.ep0.misc.bits.callback;
// Get the number of bytes received
count = USB_Ep0GetCount();
// If the call to USBD_Read() did not give a large enough buffer to hold this
// data, set the outPacketPending flag and signal an RX overrun.
if (myUsbDevice.ep0.remaining < count)
{
myUsbDevice.ep0.state = D_EP_IDLE;
myUsbDevice.ep0.misc.bits.outPacketPending = true;
status = USB_STATUS_EP_RX_BUFFER_OVERRUN;
}
else
{
USB_ReadFIFO(0, count, myUsbDevice.ep0.buf);
myUsbDevice.ep0.buf += count;
myUsbDevice.ep0.remaining -= count;
status = USB_STATUS_OK;
// If the last packet of the transfer is exactly the maximum EP0 packet
// size, we will must wait to receive a ZLP (zero-length packet) after the
// last data packet. This signals that the host has completed the transfer.
// Check for the ZLP packet case here.
if ((myUsbDevice.ep0.remaining == 0) && (count != USB_EP0_SIZE))
{
USB_Ep0SetLastOutPacketReady();
myUsbDevice.ep0.state = D_EP_IDLE;
myUsbDevice.ep0.misc.bits.callback = false;
}
else
{
// Do not call USB_Ep0SetLastOutPacketReady() until we get the ZLP.
USB_Ep0ServicedOutPacketReady();
}
}
// Make callback if requested
if (callback == true)
{
USBD_XferCompleteCb(EP0, status, count, myUsbDevice.ep0.remaining);
}
}
/***************************************************************************//**
* @brief Send a procedural stall on Endpoint 0
******************************************************************************/
void SendEp0Stall(void)
{
USB_SetIndex(0);
myUsbDevice.ep0.state = D_EP_STALL;
USB_Ep0SendStall();
}
// This function is called from USBD_Init(). It forces the user project to pull
// this module from the library so that the declared ISR can be seen and
// included. If this is not done then this entire module by never be included
// and the ISR will not be present.
void forceModuleLoad_usbint(void){}