1029 lines
35 KiB
C
1029 lines
35 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>
|
|
|
|
extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG);
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Function Prototypes
|
|
|
|
// -------------------------------
|
|
// Memory-specific FIFO access functions
|
|
#ifdef SI_GPTR
|
|
|
|
static void USB_ReadFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA), uint8_t fifoNum);
|
|
static void USB_WriteFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA));
|
|
|
|
static void USB_ReadFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA), uint8_t fifoNum);
|
|
static void USB_WriteFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA));
|
|
|
|
#if SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA
|
|
static void USB_ReadFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA), uint8_t fifoNum);
|
|
static void USB_WriteFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA));
|
|
#endif
|
|
|
|
#if SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA
|
|
static void USB_ReadFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA), uint8_t fifoNum);
|
|
static void USB_WriteFIFO_Data(uint8_t numBytes, uint8_t SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA));
|
|
#endif
|
|
|
|
static void USB_WriteFIFO_Code(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_CODE));
|
|
|
|
#else
|
|
|
|
// -------------------------------
|
|
// Generic FIFO access functions
|
|
static void USB_ReadFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint8_t fifoNum);
|
|
static void USB_WriteFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC));
|
|
|
|
#endif // #ifdef SI_GPTR
|
|
|
|
#if (SLAB_USB_EP3OUT_USED && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC))
|
|
static void memclearXdata(SI_VARIABLE_SEGMENT_POINTER(s, uint8_t, SI_SEG_XDATA),
|
|
uint16_t n);
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Functions
|
|
|
|
/***************************************************************************//**
|
|
* @brief Reads Isochronous data from the Endpoint FIFO
|
|
* @param fifoNum
|
|
* USB Endpoint FIFO to read
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to buffer to hold data read from the FIFO
|
|
******************************************************************************/
|
|
#if (SLAB_USB_EP3OUT_USED && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC) && (SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 255))
|
|
// ----------------------------------------------------------------------------
|
|
// If Isochronous mode is enabled and the max packet size is greater than 255,
|
|
// break the FIFO reads up into multiple reads of 255 or less bytes.
|
|
// ----------------------------------------------------------------------------
|
|
void USB_ReadFIFOIso(uint8_t fifoNum, uint16_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC))
|
|
{
|
|
uint8_t numBytesRead;
|
|
|
|
// USB_ReadFIFO() accepts a maximum of 255 bytes. If the number of bytes to
|
|
// send is greated than 255, call USB_ReadFIFO() multiple times.
|
|
while (numBytes > 0)
|
|
{
|
|
numBytesRead = (numBytes > 255) ? 255 : numBytes;
|
|
USB_ReadFIFO(fifoNum, numBytesRead, dat);
|
|
numBytes -= numBytesRead;
|
|
dat += numBytesRead;
|
|
}
|
|
}
|
|
#else
|
|
#define USB_ReadFIFOIso(a, b, c) USB_ReadFIFO(a, b, c)
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes Isochronous data to the Endpoint FIFO
|
|
* @param fifoNum
|
|
* USB Endpoint FIFO to write
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to buffer hoding data to write to the FIFO
|
|
******************************************************************************/
|
|
#if (SLAB_USB_EP3IN_USED && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC) && (SLAB_USB_EP3IN_MAX_PACKET_SIZE > 255))
|
|
// ----------------------------------------------------------------------------
|
|
// If Isochronous mode is enabled and the max packet size is greater than 255,
|
|
// break the FIFO writes up into multiple writes of 255 or less bytes.
|
|
// ----------------------------------------------------------------------------
|
|
void USB_WriteFIFOIso(uint8_t fifoNum, uint16_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC))
|
|
{
|
|
uint8_t numBytesWrite;
|
|
|
|
// USB_WriteFIFO() accepts a maximum of 255 bytes. If the number of bytes to
|
|
// send is greated than 255, call USB_WriteFIFO() multiple times.
|
|
while (numBytes > 0)
|
|
{
|
|
numBytesWrite = (numBytes > 255) ? 255 : numBytes;
|
|
numBytes -= numBytesWrite;
|
|
USB_WriteFIFO(fifoNum, numBytesWrite, dat, (numBytes == 0));
|
|
dat += numBytesWrite;
|
|
}
|
|
}
|
|
#else
|
|
#define USB_WriteFIFOIso(a, b, c) USB_WriteFIFO(a, b, c, true)
|
|
#endif
|
|
|
|
#if SLAB_USB_EP1IN_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 1 IN transfer interrupt
|
|
* @note This function takes no parameters, but it uses the EP1IN status
|
|
* variables stored in @ref myUsbDevice.ep1in.
|
|
******************************************************************************/
|
|
void handleUsbIn1Int(void)
|
|
{
|
|
uint8_t xferred;
|
|
bool callback;
|
|
|
|
USB_SetIndex(1);
|
|
|
|
if (USB_EpnInGetSentStall())
|
|
{
|
|
USB_EpnInClearSentStall();
|
|
}
|
|
else if (myUsbDevice.ep1in.state == D_EP_TRANSMITTING)
|
|
{
|
|
xferred = (myUsbDevice.ep1in.remaining > SLAB_USB_EP1IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP1IN_MAX_PACKET_SIZE : myUsbDevice.ep1in.remaining;
|
|
myUsbDevice.ep1in.remaining -= xferred;
|
|
myUsbDevice.ep1in.buf += xferred;
|
|
|
|
callback = myUsbDevice.ep1in.misc.bits.callback;
|
|
|
|
// Load more data
|
|
if (myUsbDevice.ep1in.remaining > 0)
|
|
{
|
|
USB_WriteFIFO(1,
|
|
(myUsbDevice.ep1in.remaining > SLAB_USB_EP1IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP1IN_MAX_PACKET_SIZE
|
|
: myUsbDevice.ep1in.remaining,
|
|
myUsbDevice.ep1in.buf,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
myUsbDevice.ep1in.misc.bits.callback = false;
|
|
myUsbDevice.ep1in.state = D_EP_IDLE;
|
|
}
|
|
|
|
if (callback == true)
|
|
{
|
|
USBD_XferCompleteCb(EP1IN, USB_STATUS_OK, xferred, myUsbDevice.ep1in.remaining);
|
|
}
|
|
|
|
}
|
|
}
|
|
#endif // SLAB_USB_EP1IN_USED
|
|
|
|
#if SLAB_USB_EP2IN_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 2 IN transfer interrupt
|
|
* @note This function takes no parameters, but it uses the EP2IN status
|
|
* variables stored in @ref myUsbDevice.ep2in.
|
|
******************************************************************************/
|
|
void handleUsbIn2Int(void)
|
|
{
|
|
uint8_t xferred;
|
|
bool callback;
|
|
|
|
USB_SetIndex(2);
|
|
|
|
if (USB_EpnInGetSentStall())
|
|
{
|
|
USB_EpnInClearSentStall();
|
|
}
|
|
else if (myUsbDevice.ep2in.state == D_EP_TRANSMITTING)
|
|
{
|
|
xferred = (myUsbDevice.ep2in.remaining > SLAB_USB_EP2IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP2IN_MAX_PACKET_SIZE : myUsbDevice.ep2in.remaining;
|
|
myUsbDevice.ep2in.remaining -= xferred;
|
|
myUsbDevice.ep2in.buf += xferred;
|
|
|
|
callback = myUsbDevice.ep2in.misc.bits.callback;
|
|
|
|
// Load more data
|
|
if (myUsbDevice.ep2in.remaining > 0)
|
|
{
|
|
USB_WriteFIFO(2,
|
|
(myUsbDevice.ep2in.remaining > SLAB_USB_EP2IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP2IN_MAX_PACKET_SIZE
|
|
: myUsbDevice.ep2in.remaining,
|
|
myUsbDevice.ep2in.buf,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
myUsbDevice.ep2in.misc.bits.callback = false;
|
|
myUsbDevice.ep2in.state = D_EP_IDLE;
|
|
}
|
|
|
|
if (callback == true)
|
|
{
|
|
USBD_XferCompleteCb(EP2IN, USB_STATUS_OK, xferred, myUsbDevice.ep2in.remaining);
|
|
}
|
|
|
|
}
|
|
}
|
|
#endif // SLAB_USB_EP2IN_USED
|
|
|
|
#if SLAB_USB_EP3IN_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 3 IN transfer interrupt
|
|
* @details Endpoint 3 IN is the only IN endpoint that supports isochronous
|
|
* transfers.
|
|
* @note This function takes no parameters, but it uses the EP3IN status
|
|
* variables stored in @ref myUsbDevice.ep3in.
|
|
******************************************************************************/
|
|
void handleUsbIn3Int(void)
|
|
{
|
|
#if SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC
|
|
uint16_t xferred, nextIdx;
|
|
#else
|
|
uint8_t xferred;
|
|
bool callback;
|
|
#endif
|
|
|
|
USB_SetIndex(3);
|
|
|
|
if (USB_EpnInGetSentStall())
|
|
{
|
|
USB_EpnInClearSentStall();
|
|
}
|
|
else if (myUsbDevice.ep3in.state == D_EP_TRANSMITTING)
|
|
{
|
|
#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR))
|
|
xferred = (myUsbDevice.ep3in.remaining > SLAB_USB_EP3IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP3IN_MAX_PACKET_SIZE : myUsbDevice.ep3in.remaining;
|
|
myUsbDevice.ep3in.remaining -= xferred;
|
|
myUsbDevice.ep3in.buf += xferred;
|
|
#endif
|
|
|
|
#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR))
|
|
|
|
callback = myUsbDevice.ep3in.misc.bits.callback;
|
|
|
|
#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)
|
|
if (myUsbDevice.ep3in.misc.bits.callback == true)
|
|
{
|
|
// In Isochronous mode, the meaning of the USBD_XferCompleteCb parameters changes:
|
|
// xferred is ignored
|
|
// remaining is the current index into the circular buffer
|
|
// the return value is the number of bytes to transmit in the next packet
|
|
xferred = USBD_XferCompleteCb(EP3IN, USB_STATUS_OK, 0, myUsbDevice.ep3inIsoIdx);
|
|
if (xferred == 0)
|
|
{
|
|
myUsbDevice.ep3in.misc.bits.inPacketPending = true;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
// Load more data
|
|
#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR))
|
|
if (myUsbDevice.ep3in.remaining > 0)
|
|
{
|
|
USB_WriteFIFO(3,
|
|
(myUsbDevice.ep3in.remaining > SLAB_USB_EP3IN_MAX_PACKET_SIZE)
|
|
? SLAB_USB_EP3IN_MAX_PACKET_SIZE
|
|
: myUsbDevice.ep3in.remaining,
|
|
myUsbDevice.ep3in.buf,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
myUsbDevice.ep3in.misc.bits.callback = false;
|
|
myUsbDevice.ep3in.state = D_EP_IDLE;
|
|
}
|
|
|
|
if (callback == true)
|
|
{
|
|
USBD_XferCompleteCb(EP3IN, USB_STATUS_OK, xferred, myUsbDevice.ep3in.remaining);
|
|
}
|
|
#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)
|
|
nextIdx = xferred + myUsbDevice.ep3inIsoIdx;
|
|
myUsbDevice.ep3in.misc.bits.inPacketPending = false;
|
|
|
|
// Check if the next index is past the end of the circular buffer.
|
|
// If so, break the write up into two calls to USB_WriteFIFOIso()
|
|
if (nextIdx > myUsbDevice.ep3in.remaining)
|
|
{
|
|
USB_WriteFIFOIso(3, myUsbDevice.ep3in.remaining - myUsbDevice.ep3inIsoIdx, &myUsbDevice.ep3in.buf[myUsbDevice.ep3inIsoIdx]);
|
|
myUsbDevice.ep3inIsoIdx = nextIdx - myUsbDevice.ep3in.remaining;
|
|
USB_WriteFIFOIso(3, myUsbDevice.ep3inIsoIdx, myUsbDevice.ep3in.buf);
|
|
}
|
|
else
|
|
{
|
|
USB_WriteFIFOIso(3, xferred, &myUsbDevice.ep3in.buf[myUsbDevice.ep3inIsoIdx]);
|
|
myUsbDevice.ep3inIsoIdx = nextIdx;
|
|
}
|
|
#endif // ( ( SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK ) || ( SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR ) )
|
|
}
|
|
}
|
|
#endif // SLAB_USB_EP3IN_USED
|
|
|
|
#if SLAB_USB_EP1OUT_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 1 OUT transfer interrupt
|
|
* @note This function takes no parameters, but it uses the EP1OUT status
|
|
* variables stored in @ref myUsbDevice.ep1out.
|
|
******************************************************************************/
|
|
void handleUsbOut1Int(void)
|
|
{
|
|
uint8_t count;
|
|
USB_Status_TypeDef status;
|
|
bool xferComplete = false;
|
|
|
|
USB_SetIndex(1);
|
|
|
|
if (USB_EpnOutGetSentStall())
|
|
{
|
|
USB_EpnOutClearSentStall();
|
|
}
|
|
else if (USB_EpnGetOutPacketReady())
|
|
{
|
|
count = USB_EpOutGetCount();
|
|
|
|
// If USBD_Read() has not been called, return an error
|
|
if (myUsbDevice.ep1out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep1out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_ERROR;
|
|
}
|
|
// Check for overrun of user buffer
|
|
else if (myUsbDevice.ep1out.remaining < count)
|
|
{
|
|
myUsbDevice.ep1out.state = D_EP_IDLE;
|
|
myUsbDevice.ep1out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_RX_BUFFER_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
USB_ReadFIFO(1, count, myUsbDevice.ep1out.buf);
|
|
|
|
myUsbDevice.ep1out.misc.bits.outPacketPending = false;
|
|
myUsbDevice.ep1out.remaining -= count;
|
|
myUsbDevice.ep1out.buf += count;
|
|
|
|
if ((myUsbDevice.ep1out.remaining == 0) || (count != SLAB_USB_EP1OUT_MAX_PACKET_SIZE))
|
|
{
|
|
myUsbDevice.ep1out.state = D_EP_IDLE;
|
|
xferComplete = true;
|
|
}
|
|
|
|
status = USB_STATUS_OK;
|
|
USB_EpnClearOutPacketReady();
|
|
}
|
|
if (myUsbDevice.ep1out.misc.bits.callback == true)
|
|
{
|
|
if (xferComplete == true)
|
|
{
|
|
myUsbDevice.ep1out.misc.bits.callback = false;
|
|
}
|
|
|
|
USBD_XferCompleteCb(EP1OUT, status, count, myUsbDevice.ep1out.remaining);
|
|
}
|
|
}
|
|
}
|
|
#endif // EP1OUT_USED
|
|
|
|
#if SLAB_USB_EP2OUT_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 2 OUT transfer interrupt
|
|
* @note This function takes no parameters, but it uses the EP2OUT status
|
|
* variables stored in @ref myUsbDevice.ep2out.
|
|
******************************************************************************/
|
|
void handleUsbOut2Int(void)
|
|
{
|
|
uint8_t count;
|
|
USB_Status_TypeDef status;
|
|
bool xferComplete = false;
|
|
|
|
USB_SetIndex(2);
|
|
|
|
if (USB_EpnOutGetSentStall())
|
|
{
|
|
USB_EpnOutClearSentStall();
|
|
}
|
|
else if (USB_EpnGetOutPacketReady())
|
|
{
|
|
count = USB_EpOutGetCount();
|
|
|
|
// If USBD_Read() has not been called, return an error
|
|
if (myUsbDevice.ep2out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep2out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_ERROR;
|
|
}
|
|
// Check for overrun of user buffer
|
|
else if (myUsbDevice.ep2out.remaining < count)
|
|
{
|
|
myUsbDevice.ep2out.state = D_EP_IDLE;
|
|
myUsbDevice.ep2out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_RX_BUFFER_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
USB_ReadFIFO(2, count, myUsbDevice.ep2out.buf);
|
|
|
|
myUsbDevice.ep2out.misc.bits.outPacketPending = false;
|
|
myUsbDevice.ep2out.remaining -= count;
|
|
myUsbDevice.ep2out.buf += count;
|
|
|
|
if ((myUsbDevice.ep2out.remaining == 0) || (count != SLAB_USB_EP2OUT_MAX_PACKET_SIZE))
|
|
{
|
|
myUsbDevice.ep2out.state = D_EP_IDLE;
|
|
xferComplete = true;
|
|
}
|
|
|
|
status = USB_STATUS_OK;
|
|
USB_EpnClearOutPacketReady();
|
|
}
|
|
if (myUsbDevice.ep2out.misc.bits.callback == true)
|
|
{
|
|
if (xferComplete == true)
|
|
{
|
|
myUsbDevice.ep2out.misc.bits.callback = false;
|
|
}
|
|
|
|
USBD_XferCompleteCb(EP2OUT, status, count, myUsbDevice.ep2out.remaining);
|
|
}
|
|
}
|
|
}
|
|
#endif // EP2OUT_USED
|
|
|
|
#if SLAB_USB_EP3OUT_USED
|
|
/***************************************************************************//**
|
|
* @brief Handle Endpoint 3 OUT transfer interrupt
|
|
* @details Endpoint 3 OUT is the only OUT endpoint that supports
|
|
* isochronous transfers.
|
|
* @note This function takes no parameters, but it uses the EP3OUT status
|
|
* variables stored in @ref myUsbDevice.ep3out.
|
|
******************************************************************************/
|
|
#if ((SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_INTR))
|
|
void handleUsbOut3Int(void)
|
|
{
|
|
uint8_t count;
|
|
USB_Status_TypeDef status;
|
|
bool xferComplete = false;
|
|
|
|
USB_SetIndex(3);
|
|
|
|
if (USB_EpnOutGetSentStall())
|
|
{
|
|
USB_EpnOutClearSentStall();
|
|
}
|
|
else if (USB_EpnGetOutPacketReady())
|
|
{
|
|
count = USB_EpOutGetCount();
|
|
|
|
// If USBD_Read() has not been called, return an error
|
|
if (myUsbDevice.ep3out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_ERROR;
|
|
}
|
|
// Check for overrun of user buffer
|
|
else if (myUsbDevice.ep3out.remaining < count)
|
|
{
|
|
myUsbDevice.ep3out.state = D_EP_IDLE;
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_RX_BUFFER_OVERRUN;
|
|
}
|
|
else
|
|
{
|
|
USB_ReadFIFO(3, count, myUsbDevice.ep3out.buf);
|
|
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = false;
|
|
myUsbDevice.ep3out.remaining -= count;
|
|
myUsbDevice.ep3out.buf += count;
|
|
|
|
if ((myUsbDevice.ep3out.remaining == 0) || (count != SLAB_USB_EP3OUT_MAX_PACKET_SIZE))
|
|
{
|
|
myUsbDevice.ep3out.state = D_EP_IDLE;
|
|
xferComplete = true;
|
|
}
|
|
|
|
status = USB_STATUS_OK;
|
|
USB_EpnClearOutPacketReady();
|
|
}
|
|
if (myUsbDevice.ep3out.misc.bits.callback == true)
|
|
{
|
|
if (xferComplete == true)
|
|
{
|
|
myUsbDevice.ep3out.misc.bits.callback = false;
|
|
}
|
|
|
|
USBD_XferCompleteCb(EP3OUT, status, count, myUsbDevice.ep3out.remaining);
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)
|
|
void handleUsbOut3Int(void)
|
|
{
|
|
uint16_t nextIdx;
|
|
uint16_t numZeroBytesFromCb;
|
|
#if (SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 255)
|
|
uint16_t count;
|
|
#else
|
|
uint8_t count;
|
|
#endif
|
|
USB_Status_TypeDef status = USB_STATUS_OK;
|
|
bool xferComplete = false;
|
|
|
|
USB_SetIndex(3);
|
|
|
|
if (USB_EpnOutGetSentStall())
|
|
{
|
|
USB_EpnOutClearSentStall();
|
|
}
|
|
else if (USB_EpnGetOutPacketReady())
|
|
{
|
|
count = USB_EpOutGetCount();
|
|
|
|
// If USBD_Read() has not been called, return an error
|
|
if (myUsbDevice.ep3out.state != D_EP_RECEIVING)
|
|
{
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = true;
|
|
status = USB_STATUS_EP_ERROR;
|
|
}
|
|
else
|
|
{
|
|
// DATERR bit set (i.e. CRC/bit-stuffing error)
|
|
if (USB_EpnGetDataError()
|
|
#ifdef SLAB_USB_ISOC_OUT_MIN_PACKET_SIZE
|
|
|| (count < SLAB_USB_ISOC_OUT_MIN_PACKET_SIZE)
|
|
#endif
|
|
#ifdef SLAB_USB_ISOC_OUT_MAX_PACKET_SIZE
|
|
|| (count > SLAB_USB_ISOC_OUT_MAX_PACKET_SIZE)
|
|
#endif
|
|
)
|
|
{
|
|
status = USB_STATUS_DATA_ERROR;
|
|
}
|
|
|
|
#ifdef SLAB_USB_ISOC_OUT_PACKETSIZE_MOD2
|
|
if ((count % 2) != 0)
|
|
{
|
|
status = USB_STATUS_DATA_ERROR;
|
|
}
|
|
#elif defined SLAB_USB_ISOC_OUT_PACKETSIZE_MOD4
|
|
if (( count % 4) != 0)
|
|
{
|
|
status = USB_STATUS_DATA_ERROR;
|
|
}
|
|
#elif defined SLAB_USB_ISOC_OUT_PACKETSIZE_MOD6
|
|
if (count % 6) != 0)
|
|
{
|
|
status = USB_STATUS_DATA_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (status == USB_STATUS_DATA_ERROR)
|
|
{
|
|
count = 0;
|
|
// Flush FIFO to get rid of bad packet
|
|
USB_EpnOutFlush();
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = false;
|
|
// Flush clears OPRDY, so no need to call USB_EpnClearOutPacketReady() now
|
|
}
|
|
else // No data error
|
|
{
|
|
nextIdx = count + myUsbDevice.ep3outIsoIdx;
|
|
|
|
// In isochronous mode, a circular buffer is used to hold the data
|
|
// If the next index into the circular buffer passes the end of the
|
|
// buffer, make two calls to USB_ReadFIFOIso()
|
|
if (nextIdx > myUsbDevice.ep3out.remaining)
|
|
{
|
|
USB_ReadFIFOIso(3, myUsbDevice.ep3out.remaining - myUsbDevice.ep3outIsoIdx, &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]);
|
|
myUsbDevice.ep3outIsoIdx = nextIdx - myUsbDevice.ep3out.remaining;
|
|
USB_ReadFIFOIso(3, myUsbDevice.ep3outIsoIdx, myUsbDevice.ep3out.buf);
|
|
}
|
|
else
|
|
{
|
|
USB_ReadFIFOIso(3, count, &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]);
|
|
myUsbDevice.ep3outIsoIdx = nextIdx;
|
|
}
|
|
|
|
myUsbDevice.ep3out.misc.bits.outPacketPending = false;
|
|
USB_EpnClearOutPacketReady();
|
|
}
|
|
}
|
|
|
|
if (myUsbDevice.ep3out.misc.bits.callback == true)
|
|
{
|
|
if (xferComplete == true)
|
|
{
|
|
myUsbDevice.ep3out.misc.bits.callback = false;
|
|
}
|
|
|
|
// In Isochronous mode, the meaning of the USBD_XferCompleteCb parameters changes:
|
|
// xferred is the number of bytes received in the last packet
|
|
// remaining is the current index into the circular buffer
|
|
numZeroBytesFromCb = USBD_XferCompleteCb(EP3OUT, status, count, myUsbDevice.ep3outIsoIdx);
|
|
|
|
// If data error occurred, the callback return value specifies how many zero-valued bytes to queue
|
|
if (numZeroBytesFromCb && (status == USB_STATUS_DATA_ERROR))
|
|
{
|
|
uint16_t numZeroBytesToWrite;
|
|
SI_SEGMENT_VARIABLE_SEGMENT_POINTER(bufPtr,
|
|
uint8_t,
|
|
SI_SEG_XDATA,
|
|
SI_SEG_DATA);
|
|
|
|
// Clear status after calling USBD_XferCompleteCb()
|
|
status = USB_STATUS_OK;
|
|
|
|
// Add the specified number of zero-value bytes
|
|
nextIdx = numZeroBytesFromCb + myUsbDevice.ep3outIsoIdx;
|
|
|
|
// Next index is past the end of the buffer (requires two writes)
|
|
if (nextIdx > myUsbDevice.ep3out.remaining)
|
|
{
|
|
// Write up to the end of the buffer
|
|
numZeroBytesToWrite = myUsbDevice.ep3out.remaining - myUsbDevice.ep3outIsoIdx;
|
|
bufPtr = &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx];
|
|
memclearXdata(bufPtr, numZeroBytesToWrite);
|
|
|
|
// Write the rest, starting at beginning of buffer
|
|
myUsbDevice.ep3outIsoIdx = nextIdx - myUsbDevice.ep3out.remaining;
|
|
numZeroBytesToWrite = myUsbDevice.ep3outIsoIdx;
|
|
bufPtr = &myUsbDevice.ep3out.buf[0];
|
|
memclearXdata(bufPtr, numZeroBytesToWrite);
|
|
}
|
|
// Next index is not past the end of the buffer
|
|
else
|
|
{
|
|
bufPtr = &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx];
|
|
memclearXdata(bufPtr, numZeroBytesFromCb);
|
|
myUsbDevice.ep3outIsoIdx = nextIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Sets all elements in a contiguous array of XDATA to zero
|
|
* @param s
|
|
* Pointer to the block of memory to fill
|
|
* @param n
|
|
* Number of bytes to be set to the value
|
|
******************************************************************************/
|
|
static void memclearXdata(SI_VARIABLE_SEGMENT_POINTER(s, uint8_t, SI_SEG_XDATA),
|
|
uint16_t n)
|
|
{
|
|
while (n)
|
|
{
|
|
*s++ = 0;
|
|
n--;
|
|
}
|
|
}
|
|
|
|
#endif // #if ((SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_INTR))
|
|
#endif // EP3OUT_USED
|
|
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO
|
|
* @param fifoNum
|
|
* USB Endpoint FIFO to read
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to buffer to hold data read from the FIFO
|
|
******************************************************************************/
|
|
void USB_ReadFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC))
|
|
{
|
|
if (numBytes > 0)
|
|
{
|
|
USB_EnableReadFIFO(fifoNum);
|
|
|
|
// Convert generic pointer to memory-specific pointer and call the
|
|
// the corresponding memory-specific function, if possible.
|
|
// The memory-specific functions are much faster than the generic functions.
|
|
#ifdef SI_GPTR
|
|
|
|
switch (((SI_GEN_PTR_t *)&dat)->gptr.memtype)
|
|
{
|
|
case SI_GPTR_MTYPE_IDATA:
|
|
USB_ReadFIFO_Idata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_IDATA))dat, fifoNum);
|
|
break;
|
|
|
|
// For some compilers, IDATA and DATA are treated the same.
|
|
// Only call the USB_ReadFIFO_Data() if the compiler differentiates
|
|
// between DATA and IDATA.
|
|
#if (SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA)
|
|
case SI_GPTR_MTYPE_DATA:
|
|
USB_ReadFIFO_Data(numBytes, dat, fifoNum);
|
|
break;
|
|
#endif
|
|
|
|
case SI_GPTR_MTYPE_XDATA:
|
|
USB_ReadFIFO_Xdata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_XDATA))dat, fifoNum);
|
|
break;
|
|
|
|
// For some compilers, XDATA and PDATA are treated the same.
|
|
// Only call the USB_ReadFIFO_Pdata() if the compiler differentiates
|
|
// between XDATA and PDATA.
|
|
#if (SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA)
|
|
case SI_GPTR_MTYPE_PDATA:
|
|
USB_ReadFIFO_Pdata(numBytes, dat, fifoNum);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#else
|
|
USB_ReadFIFO_Generic(numBytes, dat, fifoNum);
|
|
#endif // #ifdef SI_GPTR
|
|
|
|
USB_DisableReadFIFO(fifoNum);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data to the USB FIFO
|
|
* @param fifoNum
|
|
* USB Endpoint FIFO to write
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to buffer hoding data to write to the FIFO
|
|
* @param txPacket
|
|
* If TRUE, the packet will be sent immediately after loading the
|
|
* FIFO
|
|
* If FALSE, the packet will be stored in the FIFO and the
|
|
* transmission must be started at a later time
|
|
******************************************************************************/
|
|
void USB_WriteFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), bool txPacket)
|
|
{
|
|
USB_EnableWriteFIFO(fifoNum);
|
|
|
|
// Convert generic pointer to memory-specific pointer and call the
|
|
// the corresponding memory-specific function, if possible.
|
|
// The memory-specific functions are much faster than the generic functions.
|
|
#ifdef SI_GPTR
|
|
|
|
switch (((SI_GEN_PTR_t *)&dat)->gptr.memtype)
|
|
{
|
|
case SI_GPTR_MTYPE_IDATA:
|
|
USB_WriteFIFO_Idata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_IDATA))dat);
|
|
break;
|
|
|
|
// For some compilers, IDATA and DATA are treated the same.
|
|
// Only call the USB_WriteFIFO_Data() if the compiler differentiates between
|
|
// DATA and IDATA.
|
|
#if (SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA)
|
|
case SI_GPTR_MTYPE_DATA:
|
|
USB_WriteFIFO_Data(numBytes, dat);
|
|
break;
|
|
#endif
|
|
|
|
case SI_GPTR_MTYPE_XDATA:
|
|
USB_WriteFIFO_Xdata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_XDATA))dat);
|
|
break;
|
|
|
|
// For some compilers, XDATA and PDATA are treated the same.
|
|
// Only call the USB_WriteFIFO_Pdata() if the compiler differentiates
|
|
// between XDATA and PDATA.
|
|
#if (SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA)
|
|
case SI_GPTR_MTYPE_PDATA:
|
|
USB_WriteFIFO_Pdata(numBytes, dat);
|
|
break;
|
|
#endif
|
|
|
|
case SI_GPTR_MTYPE_CODE:
|
|
USB_WriteFIFO_Code(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_CODE))dat);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#else
|
|
USB_WriteFIFO_Generic(numBytes, dat);
|
|
#endif // #ifdef SI_GPTR
|
|
|
|
USB_DisableWriteFIFO(fifoNum);
|
|
|
|
if ((txPacket == true) && (fifoNum > 0))
|
|
{
|
|
USB_SetIndex(fifoNum);
|
|
USB_EpnSetInPacketReady();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Memory-Specific FIFO Access Functions
|
|
//
|
|
// Memory-specific functions are much faster (more than 2x) than generic
|
|
// generic functions, so we will use memory-specific functions if possible.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#ifdef SI_GPTR
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO to a buffer in IRAM
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to IDATA buffer to hold data read from the FIFO
|
|
* @param fifoNum
|
|
* USB FIFO to read
|
|
******************************************************************************/
|
|
static void USB_ReadFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA), uint8_t fifoNum)
|
|
{
|
|
while (--numBytes)
|
|
{
|
|
USB_GetFIFOByte(dat);
|
|
dat++;
|
|
}
|
|
USB_GetLastFIFOByte(dat, fifoNum);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in IRAM to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to IDATA buffer holding data to write to the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO to a buffer in XRAM
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to XDATA buffer to hold data read from the FIFO
|
|
* @param fifoNum
|
|
* USB FIFO to read
|
|
******************************************************************************/
|
|
static void USB_ReadFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA), uint8_t fifoNum)
|
|
{
|
|
while (--numBytes)
|
|
{
|
|
USB_GetFIFOByte(dat);
|
|
dat++;
|
|
}
|
|
USB_GetLastFIFOByte(dat, fifoNum);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in XRAM to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to XDATA buffer holding data to write to the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
|
|
#if SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO to a buffer in paged XRAM
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to PDATA buffer to hold data read from the FIFO
|
|
* @param fifoNum
|
|
* USB FIFO to read
|
|
******************************************************************************/
|
|
static void USB_ReadFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA), uint8_t fifoNum)
|
|
{
|
|
while (--numBytes)
|
|
{
|
|
USB_GetFIFOByte(dat);
|
|
dat++;
|
|
}
|
|
USB_GetLastFIFOByte(dat, fifoNum);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in paged XRAM to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to PDATA buffer holding data to write to the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO to a buffer in DRAM
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to DATA buffer to hold data read from the FIFO
|
|
* @param fifoNum
|
|
* USB FIFO to read
|
|
******************************************************************************/
|
|
static void USB_ReadFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA), uint8_t fifoNum)
|
|
{
|
|
while (--numBytes)
|
|
{
|
|
USB_GetFIFOByte(dat);
|
|
dat++;
|
|
}
|
|
USB_GetLastFIFOByte(dat, fifoNum);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in DRAM to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to DATA buffer to hold data read from the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in code space to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to CODE buffer holding data to write to the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Code(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_CODE))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
|
|
#else
|
|
/***************************************************************************//**
|
|
* @brief Reads data from the USB FIFO to a buffer in generic memory space
|
|
* @param numBytes
|
|
* Number of bytes to read from the FIFO
|
|
* @param dat
|
|
* Pointer to generic buffer to hold data read from the FIFO
|
|
* @param fifoNum
|
|
* USB FIFO to read
|
|
******************************************************************************/
|
|
static void USB_ReadFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint8_t fifoNum)
|
|
{
|
|
while (--numBytes)
|
|
{
|
|
USB_GetFIFOByte(dat);
|
|
dat++;
|
|
}
|
|
USB_GetLastFIFOByte(dat, fifoNum);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief Writes data held in generic memory space to the USB FIFO
|
|
* @details The FIFO to write must be set before calling the function with
|
|
* @ref USB_EnableWriteFIFO().
|
|
* @param numBytes
|
|
* Number of bytes to write to the FIFO
|
|
* @param dat
|
|
* Pointer to generic buffer holding data to write to the FIFO
|
|
******************************************************************************/
|
|
static void USB_WriteFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC))
|
|
{
|
|
while (numBytes--)
|
|
{
|
|
USB_SetFIFOByte(*dat);
|
|
dat++;
|
|
}
|
|
}
|
|
|
|
#endif // #ifdef SI_GPTR
|