Files
simtrace2/firmware/atmel_softpack_libraries/libchip_sam3s/source/USBD_HAL.c
Harald Welte 3f5e3ddffc Change directory structure to align with Atmel softpack
This way we can easily check with 'diff' for differences in our code and
Atmel softpack.  Also, this layout is more suitable for building various
different firmware images (e.g. factory-test, dfu-loader, main
application) for a variety of different boards (simtrace, owhw, qmod).
2017-02-27 14:24:11 +01:00

1703 lines
54 KiB
C

/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/**
\file
\section Purpose
Implementation of USB device functions on a UDP controller.
See \ref usbd_api_method USBD API Methods.
*/
/** \addtogroup usbd_hal
*@{*/
/*---------------------------------------------------------------------------
* Headers
*---------------------------------------------------------------------------*/
#ifdef TRACE_LEVEL
#undef TRACE_LEVEL
#endif
#define TRACE_LEVEL TRACE_LEVEL_WARNING
#include "chip.h"
#include "USBD_HAL.h"
#include <usb/device/dfu/dfu.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/*---------------------------------------------------------------------------
* Definitions
*---------------------------------------------------------------------------*/
/** Indicates chip has an UDP Full Speed. */
#define CHIP_USB_UDP
/** Indicates chip has an internal pull-up. */
#define CHIP_USB_PULLUP_INTERNAL
/** Number of USB endpoints */
#define CHIP_USB_NUMENDPOINTS 8
/** Endpoints max paxcket size */
#define CHIP_USB_ENDPOINTS_MAXPACKETSIZE(i) \
((i == 0) ? 64 : \
((i == 1) ? 64 : \
((i == 2) ? 64 : \
((i == 3) ? 64 : \
((i == 4) ? 512 : \
((i == 5) ? 512 : \
((i == 6) ? 64 : \
((i == 7) ? 64 : 0 ))))))))
/** Endpoints Number of Bank */
#define CHIP_USB_ENDPOINTS_BANKS(i) \
((i == 0) ? 1 : \
((i == 1) ? 2 : \
((i == 2) ? 2 : \
((i == 3) ? 1 : \
((i == 4) ? 2 : \
((i == 5) ? 2 : \
((i == 6) ? 2 : \
((i == 7) ? 2 : 0 ))))))))
/**
* \section UDP_registers_sec "UDP Register field values"
*
* This section lists the initialize values of UDP registers.
*
* \subsection Values
* - UDP_RXDATA
*/
/** Bit mask for both banks of the UDP_CSR register. */
#define UDP_CSR_RXDATA_BK (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1)
/**
* \section endpoint_states_sec "UDP Endpoint states"
*
* This page lists the endpoint states.
*
* \subsection States
* - UDP_ENDPOINT_DISABLED
* - UDP_ENDPOINT_HALTED
* - UDP_ENDPOINT_IDLE
* - UDP_ENDPOINT_SENDING
* - UDP_ENDPOINT_RECEIVING
* - UDP_ENDPOINT_SENDINGM
* - UDP_ENDPOINT_RECEIVINGM
*/
/** Endpoint states: Endpoint is disabled */
#define UDP_ENDPOINT_DISABLED 0
/** Endpoint states: Endpoint is halted (i.e. STALLs every request) */
#define UDP_ENDPOINT_HALTED 1
/** Endpoint states: Endpoint is idle (i.e. ready for transmission) */
#define UDP_ENDPOINT_IDLE 2
/** Endpoint states: Endpoint is sending data */
#define UDP_ENDPOINT_SENDING 3
/** Endpoint states: Endpoint is receiving data */
#define UDP_ENDPOINT_RECEIVING 4
/** Endpoint states: Endpoint is sending MBL */
#define UDP_ENDPOINT_SENDINGM 5
/** Endpoint states: Endpoint is receiving MBL */
#define UDP_ENDPOINT_RECEIVINGM 6
/**
* \section udp_csr_register_access_sec "UDP CSR register access"
*
* This page lists the macros to access UDP CSR register.
*
* \comment
* In a preemptive environment, set or clear the flag and wait for a time of
* 1 UDPCK clock cycle and 1 peripheral clock cycle. However, RX_DATA_BK0,
* TXPKTRDY, RX_DATA_BK1 require wait times of 3 UDPCK clock cycles and
* 5 peripheral clock cycles before accessing DPR.
* See datasheet
*
* !Macros
* - CLEAR_CSR
* - SET_CSR
*/
#if defined ( __CC_ARM )
#define nop() {volatile int h; for(h=0;h<10;h++){}}
#elif defined ( __ICCARM__ )
#include <intrinsics.h>
#define nop() (__no_operation())
#elif defined ( __GNUC__ )
#define nop() __asm__ __volatile__ ( "nop" )
#endif
/** Bitmap for all status bits in CSR. */
#define REG_NO_EFFECT_1_ALL UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 \
|UDP_CSR_STALLSENTISOERROR | UDP_CSR_RXSETUP \
|UDP_CSR_TXCOMP
/**
* Sets the specified bit(s) in the UDP_CSR register.
*
* \param endpoint The endpoint number of the CSR to process.
* \param flags The bitmap to set to 1.
*/
#define SET_CSR(endpoint, flags) \
{ \
volatile uint32_t reg; \
int32_t nop_count ; \
reg = UDP->UDP_CSR[endpoint] ; \
reg |= REG_NO_EFFECT_1_ALL; \
reg |= (flags); \
UDP->UDP_CSR[endpoint] = reg; \
for( nop_count=0; nop_count<15; nop_count++ ) {\
nop();\
}\
}
/**
* Clears the specified bit(s) in the UDP_CSR register.
*
* \param endpoint The endpoint number of the CSR to process.
* \param flags The bitmap to clear to 0.
*/
#define CLEAR_CSR(endpoint, flags) \
{ \
volatile uint32_t reg; \
int32_t nop_count ; \
reg = UDP->UDP_CSR[endpoint]; \
reg |= REG_NO_EFFECT_1_ALL; \
reg &= ~((uint32_t)(flags)); \
UDP->UDP_CSR[endpoint] = reg; \
for( nop_count=0; nop_count<15; nop_count++ ) {\
nop();\
}\
}
/** Get Number of buffer in Multi-Buffer-List
* \param i input index
* \param o output index
* \param size list size
*/
#define MBL_NbBuffer(i, o, size) (((i)>(o))?((i)-(o)):((i)+(size)-(o)))
/** Buffer list is full */
#define MBL_FULL 1
/** Buffer list is null */
#define MBL_NULL 2
/*---------------------------------------------------------------------------
* Types
*---------------------------------------------------------------------------*/
/** Describes header for UDP endpoint transfer. */
typedef struct {
/** Optional callback to invoke when the transfer completes. */
void* fCallback;
/** Optional argument to the callback function. */
void* pArgument;
/** Transfer type */
uint8_t transType;
} TransferHeader;
/** Describes a transfer on a UDP endpoint. */
typedef struct {
/** Optional callback to invoke when the transfer completes. */
TransferCallback fCallback;
/** Optional argument to the callback function. */
void *pArgument;
/** Transfer type */
uint16_t transType;
/** Number of bytes which have been written into the UDP internal FIFO
* buffers. */
int16_t buffered;
/** Pointer to a data buffer used for emission/reception. */
uint8_t *pData;
/** Number of bytes which have been sent/received. */
int32_t transferred;
/** Number of bytes which have not been buffered/transferred yet. */
int32_t remaining;
} Transfer;
/** Describes Multi Buffer List transfer on a UDP endpoint. */
typedef struct {
/** Optional callback to invoke when the transfer completes. */
MblTransferCallback fCallback;
/** Optional argument to the callback function. */
void *pArgument;
/** Transfer type */
volatile uint8_t transType;
/** List state (OK, FULL, NULL) (run time) */
uint8_t listState;
/** Multi-Buffer List size */
uint16_t listSize;
/** Pointer to multi-buffer list */
USBDTransferBuffer *pMbl;
/** Offset number of buffers to start transfer */
uint16_t offsetSize;
/** Current processing buffer index (run time) */
uint16_t outCurr;
/** Loast loaded buffer index (run time) */
uint16_t outLast;
/** Current buffer for input (run time) */
uint16_t inCurr;
} MblTransfer;
/**
* Describes the state of an endpoint of the UDP controller.
*/
typedef struct {
/* CSR */
//uint32_t CSR;
/** Current endpoint state. */
volatile uint8_t state;
/** Current reception bank (0 or 1). */
volatile uint8_t bank;
/** Maximum packet size for the endpoint. */
volatile uint16_t size;
/** Describes an ongoing transfer (if current state is either
* UDP_ENDPOINT_SENDING or UDP_ENDPOINT_RECEIVING) */
union {
TransferHeader transHdr;
Transfer singleTransfer;
MblTransfer mblTransfer;
} transfer;
} Endpoint;
/*---------------------------------------------------------------------------
* Internal variables
*---------------------------------------------------------------------------*/
/** Holds the internal state for each endpoint of the UDP. */
static Endpoint endpoints[CHIP_USB_NUMENDPOINTS];
/*---------------------------------------------------------------------------
* Internal Functions
*---------------------------------------------------------------------------*/
/**
* Enables the clock of the UDP peripheral.
* \return 1 if peripheral status changed.
*/
static uint8_t UDP_EnablePeripheralClock(void)
{
if (!PMC_IsPeriphEnabled(ID_UDP)) {
PMC_EnablePeripheral(ID_UDP);
return 1;
}
return 0;
}
/**
* Disables the UDP peripheral clock.
*/
static inline void UDP_DisablePeripheralClock(void)
{
PMC_DisablePeripheral(ID_UDP);
}
/**
* Enables the 48MHz USB clock.
*/
static inline void UDP_EnableUsbClock(void)
{
REG_PMC_SCER = PMC_SCER_UDP;
}
/**
* Disables the 48MHz USB clock.
*/
static inline void UDP_DisableUsbClock(void)
{
REG_PMC_SCDR = PMC_SCER_UDP;
}
/**
* Enables the UDP transceiver.
*/
static inline void UDP_EnableTransceiver(void)
{
UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_TXVDIS;
}
/**
* Disables the UDP transceiver.
*/
static inline void UDP_DisableTransceiver(void)
{
UDP->UDP_TXVC |= UDP_TXVC_TXVDIS;
}
/**
* Handles a completed transfer on the given endpoint, invoking the
* configured callback if any.
* \param bEndpoint Number of the endpoint for which the transfer has completed.
* \param bStatus Status code returned by the transfer operation
*/
static void UDP_EndOfTransfer(uint8_t bEndpoint, uint8_t bStatus)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
// Check that endpoint was sending or receiving data
if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING)
|| (pEndpoint->state == UDP_ENDPOINT_SENDING)) {
Transfer *pTransfer = (Transfer *)&(pEndpoint->transfer);
uint32_t transferred = pTransfer->transferred;
uint32_t remaining = pTransfer->remaining + pTransfer->buffered;
TRACE_DEBUG_WP("EoT ");
/* Endpoint returns in Idle state */
pEndpoint->state = UDP_ENDPOINT_IDLE;
/* Reset descriptor values */
pTransfer->pData = 0;
pTransfer->transferred = -1;
pTransfer->buffered = -1;
pTransfer->remaining = -1;
// Invoke callback is present
if (pTransfer->fCallback != 0) {
((TransferCallback) pTransfer->fCallback)
(pTransfer->pArgument,
bStatus,
transferred,
remaining);
}
else {
TRACE_DEBUG_WP("NoCB ");
}
}
else if ( (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM)
|| (pEndpoint->state == UDP_ENDPOINT_SENDINGM) ) {
MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer);
TRACE_DEBUG_WP("EoMT ");
/* Endpoint returns in Idle state */
pEndpoint->state = UDP_ENDPOINT_IDLE;
/* Reset transfer descriptor */
if (pTransfer->transType) {
MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer);
pMblt->listState = 0;
pMblt->outCurr = pMblt->inCurr = pMblt->outLast = 0;
}
/* Invoke callback */
if (pTransfer->fCallback != 0) {
((MblTransferCallback) pTransfer->fCallback)
(pTransfer->pArgument,
bStatus);
}
else {
TRACE_DEBUG_WP("NoCB ");
}
}
}
/**
* Clears the correct reception flag (bank 0 or bank 1) of an endpoint
* \param bEndpoint Index of endpoint
*/
static void UDP_ClearRxFlag(uint8_t bEndpoint)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
// Clear flag and change banks
if (pEndpoint->bank == 0) {
CLEAR_CSR(bEndpoint, UDP_CSR_RX_DATA_BK0);
// Swap bank if in dual-fifo mode
if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) {
pEndpoint->bank = 1;
}
}
else {
CLEAR_CSR(bEndpoint, UDP_CSR_RX_DATA_BK1);
pEndpoint->bank = 0;
}
}
/**
* Update multi-buffer-transfer descriptors.
* \param pTransfer Pointer to instance MblTransfer.
* \param size Size of bytes that processed.
* \param forceEnd Force the buffer END.
* \return 1 if current buffer ended.
*/
static uint8_t UDP_MblUpdate(MblTransfer *pTransfer,
USBDTransferBuffer * pBi,
uint16_t size,
uint8_t forceEnd)
{
/* Update transfer descriptor */
pBi->remaining -= size;
/* Check if list NULL */
if (pTransfer->listState == MBL_NULL) {
return 1;
}
/* Check if current buffer ended */
if (pBi->remaining == 0 || forceEnd || size == 0) {
/* Process to next buffer */
if ((++ pTransfer->outCurr) == pTransfer->listSize)
pTransfer->outCurr = 0;
/* Check buffer NULL case */
if (pTransfer->outCurr == pTransfer->inCurr)
pTransfer->listState = MBL_NULL;
else {
pTransfer->listState = 0;
/* Continue transfer, prepare for next operation */
pBi = &pTransfer->pMbl[pTransfer->outCurr];
pBi->buffered = 0;
pBi->transferred = 0;
pBi->remaining = pBi->size;
}
return 1;
}
return 0;
}
/**
* Transfers a data payload from the current tranfer buffer to the endpoint
* FIFO
* \param bEndpoint Number of the endpoint which is sending data.
*/
static uint8_t UDP_MblWriteFifo(uint8_t bEndpoint)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer);
USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->outCurr]);
int32_t size;
volatile uint8_t * pBytes;
volatile uint8_t bufferEnd = 1;
/* Get the number of bytes to send */
size = pEndpoint->size;
if (size > pBi->remaining) size = pBi->remaining;
TRACE_DEBUG_WP("w%d.%" PRId32 " ", pTransfer->outCurr, size);
/* Record last accessed buffer */
pTransfer->outLast = pTransfer->outCurr;
pBytes = &(pBi->pBuffer[pBi->transferred + pBi->buffered]);
pBi->buffered += size;
bufferEnd = UDP_MblUpdate(pTransfer, pBi, size, 0);
/* Write packet in the FIFO buffer */
if (size) {
int32_t c8 = size >> 3;
int32_t c1 = size & 0x7;
for (; c8; c8 --) {
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
}
for (; c1; c1 --) {
UDP->UDP_FDR[bEndpoint] = *(pBytes ++);
}
}
return bufferEnd;
}
/**
* Transfers a data payload from the current tranfer buffer to the endpoint
* FIFO
* \param bEndpoint Number of the endpoint which is sending data.
*/
static void UDP_WritePayload(uint8_t bEndpoint)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
int32_t size;
// Get the number of bytes to send
size = pEndpoint->size;
if (size > pTransfer->remaining) {
size = pTransfer->remaining;
}
// Update transfer descriptor information
pTransfer->buffered += size;
pTransfer->remaining -= size;
// Write packet in the FIFO buffer
while (size > 0) {
UDP->UDP_FDR[bEndpoint] = *(pTransfer->pData);
pTransfer->pData++;
size--;
}
}
/**
* Transfers a data payload from an endpoint FIFO to the current transfer buffer
* \param bEndpoint Endpoint number.
* \param wPacketSize Size of received data packet
*/
static void UDP_ReadPayload(uint8_t bEndpoint, int32_t wPacketSize)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
// Check that the requested size is not bigger than the remaining transfer
if (wPacketSize > pTransfer->remaining) {
pTransfer->buffered += wPacketSize - pTransfer->remaining;
wPacketSize = pTransfer->remaining;
}
// Update transfer descriptor information
pTransfer->remaining -= wPacketSize;
pTransfer->transferred += wPacketSize;
// Retrieve packet
while (wPacketSize > 0) {
*(pTransfer->pData) = (uint8_t) UDP->UDP_FDR[bEndpoint];
pTransfer->pData++;
wPacketSize--;
}
}
/**
* Received SETUP packet from endpoint 0 FIFO
* \param pRequest Generic USB SETUP request sent over Control endpoints
*/
static void UDP_ReadRequest(USBGenericRequest *pRequest)
{
uint8_t *pData = (uint8_t *)pRequest;
uint32_t i;
// Copy packet
for (i = 0; i < 8; i++) {
*pData = (uint8_t) UDP->UDP_FDR[0];
pData++;
}
}
/**
* Checks if an ongoing transfer on an endpoint has been completed.
* \param bEndpoint Endpoint number.
* \return 1 if the current transfer on the given endpoint is complete;
* otherwise 0.
*/
static uint8_t UDP_IsTransferFinished(uint8_t bEndpoint)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
// Check if it is a Control endpoint
// -> Control endpoint must always finish their transfer with a zero-length
// packet
if ((UDP->UDP_CSR[bEndpoint] & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_CTRL) {
return (pTransfer->buffered < pEndpoint->size);
}
// Other endpoints only need to transfer all the data
else {
return (pTransfer->buffered <= pEndpoint->size)
&& (pTransfer->remaining == 0);
}
}
/**
* Endpoint interrupt handler.
* Handle IN/OUT transfers, received SETUP packets and STALLing
* \param bEndpoint Index of endpoint
*/
static void UDP_EndpointHandler(uint8_t bEndpoint)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer);
uint32_t status = UDP->UDP_CSR[bEndpoint];
uint16_t wPacketSize;
USBGenericRequest request;
TRACE_DEBUG_WP("E%d ", bEndpoint);
TRACE_DEBUG_WP("st:0x%" PRIX32 " ", status);
// Handle interrupts
// IN packet sent
if ((status & UDP_CSR_TXCOMP) != 0) {
TRACE_DEBUG_WP("Wr ");
// Check that endpoint was in MBL Sending state
if (pEndpoint->state == UDP_ENDPOINT_SENDINGM) {
USBDTransferBuffer * pMbli = &(pMblt->pMbl[pMblt->outLast]);
uint8_t bufferEnd = 0;
TRACE_DEBUG_WP("TxM%d.%d ", pMblt->listState, pMbli->buffered);
// End of transfer ?
if (pMblt->listState == MBL_NULL && pMbli->buffered == 0) {
pMbli->transferred += pMbli->buffered;
pMbli->buffered = 0;
// Disable interrupt
UDP->UDP_IDR = 1 << bEndpoint;
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
}
else {
// Transfer remaining data
TRACE_DEBUG_WP("%d ", pEndpoint->size);
if (pMbli->buffered > pEndpoint->size) {
pMbli->transferred += pEndpoint->size;
pMbli->buffered -= pEndpoint->size;
}
else {
pMbli->transferred += pMbli->buffered;
pMbli->buffered = 0;
}
// Send next packet
if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) {
// No double buffering
bufferEnd = UDP_MblWriteFifo(bEndpoint);
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
}
else {
// Double buffering
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
bufferEnd = UDP_MblWriteFifo(bEndpoint);
}
if (bufferEnd && pMblt->fCallback) {
((MblTransferCallback) pTransfer->fCallback)
(pTransfer->pArgument,
USBD_STATUS_PARTIAL_DONE);
}
}
}
// Check that endpoint was in Sending state
else if (pEndpoint->state == UDP_ENDPOINT_SENDING) {
// End of transfer ?
if (UDP_IsTransferFinished(bEndpoint)) {
pTransfer->transferred += pTransfer->buffered;
pTransfer->buffered = 0;
// Disable interrupt if this is not a control endpoint
if ((status & UDP_CSR_EPTYPE_Msk) != UDP_CSR_EPTYPE_CTRL) {
UDP->UDP_IDR = 1 << bEndpoint;
}
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
}
else {
// Transfer remaining data
TRACE_DEBUG_WP(" %d ", pEndpoint->size);
pTransfer->transferred += pEndpoint->size;
pTransfer->buffered -= pEndpoint->size;
// Send next packet
if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) {
// No double buffering
UDP_WritePayload(bEndpoint);
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
}
else {
// Double buffering
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
UDP_WritePayload(bEndpoint);
}
}
}
else {
// Acknowledge interrupt
TRACE_ERROR("Error Wr%d, %x\n\r", bEndpoint, pEndpoint->state);
CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP);
}
}
// OUT packet received
if ((status & UDP_CSR_RXDATA_BK) != 0) {
TRACE_DEBUG_WP("Rd ");
// Check that the endpoint is in Receiving state
if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) {
// Check if an ACK has been received on a Control endpoint
if (((status & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_CTRL)
&& ((status & UDP_CSR_RXBYTECNT_Msk) == 0)) {
// Acknowledge the data and finish the current transfer
UDP_ClearRxFlag(bEndpoint);
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS);
}
// Check if the data has been STALLed
else if ((status & UDP_CSR_FORCESTALL) != 0) {
// Discard STALLed data
TRACE_DEBUG_WP("Discard ");
UDP_ClearRxFlag(bEndpoint);
}
// NAK the data
else {
TRACE_DEBUG_WP("Nak ");
UDP->UDP_IDR = 1 << bEndpoint;
}
}
// Endpoint is in Read state
else {
// Retrieve data and store it into the current transfer buffer
wPacketSize = (uint16_t) (status >> 16);
TRACE_DEBUG_WP("%d ", wPacketSize);
UDP_ReadPayload(bEndpoint, wPacketSize);
UDP_ClearRxFlag(bEndpoint);
// Check if the transfer is finished
if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) {
// Disable interrupt if this is not a control endpoint
if ((status & UDP_CSR_EPTYPE_Msk) != UDP_CSR_EPTYPE_CTRL) {
UDP->UDP_IDR = 1 << bEndpoint;
}
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS);
}
}
}
// STALL sent
if ((status & UDP_CSR_STALLSENTISOERROR) != 0) {
CLEAR_CSR(bEndpoint, UDP_CSR_STALLSENTISOERROR);
if ( (status & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_ISO_IN
|| (status & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_ISO_OUT ) {
TRACE_WARNING("Isoe [%d] ", bEndpoint);
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED);
}
else {
TRACE_WARNING("Sta 0x%X [%d] ", (int)status, bEndpoint);
if (pEndpoint->state != UDP_ENDPOINT_HALTED) {
TRACE_WARNING( "_ " );
// If the endpoint is not halted, clear the STALL condition
CLEAR_CSR(bEndpoint, UDP_CSR_FORCESTALL);
}
}
}
// SETUP packet received
if ((status & UDP_CSR_RXSETUP) != 0) {
TRACE_DEBUG_WP("Stp ");
// If a transfer was pending, complete it
// Handles the case where during the status phase of a control write
// transfer, the host receives the device ZLP and ack it, but the ack
// is not received by the device
if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING)
|| (pEndpoint->state == UDP_ENDPOINT_SENDING)) {
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS);
}
// Copy the setup packet
UDP_ReadRequest(&request);
// Set the DIR bit before clearing RXSETUP in Control IN sequence
if (USBGenericRequest_GetDirection(&request) == USBGenericRequest_IN) {
SET_CSR(bEndpoint, UDP_CSR_DIR);
}
// Acknowledge setup packet
CLEAR_CSR(bEndpoint, UDP_CSR_RXSETUP);
// Forward the request to the upper layer
USBD_RequestHandler(0, &request);
}
}
/**
* Sends data through a USB endpoint. Sets up the transfer descriptor,
* writes one or two data payloads (depending on the number of FIFO bank
* for the endpoint) and then starts the actual transfer. The operation is
* complete when all the data has been sent.
*
* *If the size of the buffer is greater than the size of the endpoint
* (or twice the size if the endpoint has two FIFO banks), then the buffer
* must be kept allocated until the transfer is finished*. This means that
* it is not possible to declare it on the stack (i.e. as a local variable
* of a function which returns after starting a transfer).
*
* \param pEndpoint Pointer to Endpoint struct.
* \param pData Pointer to a buffer with the data to send.
* \param dLength Size of the data buffer.
* \return USBD_STATUS_SUCCESS if the transfer has been started;
* otherwise, the corresponding error status code.
*/
static inline uint8_t UDP_Write(uint8_t bEndpoint,
const void *pData,
uint32_t dLength)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
/* Check that the endpoint is in Idle state */
if (pEndpoint->state != UDP_ENDPOINT_IDLE) {
return USBD_STATUS_LOCKED;
}
TRACE_DEBUG_WP("Write%d(%" PRIu32 ") ", bEndpoint, dLength);
/* int i;
for (i = 0; i < dLength; i++) {
if (!(i%16)) {
printf("\n\r");
}
printf("0x%x ", ((uint8_t*)pData)[i]);
}
printf("\n\r");
*/
/* Setup the transfer descriptor */
pTransfer->pData = (void *) pData;
pTransfer->remaining = dLength;
pTransfer->buffered = 0;
pTransfer->transferred = 0;
/* Send the first packet */
pEndpoint->state = UDP_ENDPOINT_SENDING;
while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY);
UDP_WritePayload(bEndpoint);
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
/* If double buffering is enabled and there is data remaining,
prepare another packet */
if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && (pTransfer->remaining > 0)) {
UDP_WritePayload(bEndpoint);
}
/* Enable interrupt on endpoint */
UDP->UDP_IER = 1 << bEndpoint;
return USBD_STATUS_SUCCESS;
}
/**
* Sends data through a USB endpoint. Sets up the transfer descriptor list,
* writes one or two data payloads (depending on the number of FIFO bank
* for the endpoint) and then starts the actual transfer. The operation is
* complete when all the transfer buffer in the list has been sent.
*
* *If the size of the buffer is greater than the size of the endpoint
* (or twice the size if the endpoint has two FIFO banks), then the buffer
* must be kept allocated until the transfer is finished*. This means that
* it is not possible to declare it on the stack (i.e. as a local variable
* of a function which returns after starting a transfer).
*
* \param pEndpoint Pointer to Endpoint struct.
* \param pData Pointer to a buffer with the data to send.
* \param dLength Size of the data buffer.
* \return USBD_STATUS_SUCCESS if the transfer has been started;
* otherwise, the corresponding error status code.
*/
static inline uint8_t UDP_AddWr(uint8_t bEndpoint,
const void *pData,
uint32_t dLength)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
MblTransfer *pMbl = (MblTransfer*)&(pEndpoint->transfer);
USBDTransferBuffer *pTx;
/* Check parameter */
if (dLength >= 0x10000)
return USBD_STATUS_INVALID_PARAMETER;
/* Data in progressing */
if (pEndpoint->state > UDP_ENDPOINT_IDLE) {
/* If list full */
if (pMbl->listState == MBL_FULL) {
return USBD_STATUS_LOCKED;
}
}
TRACE_DEBUG_WP("AddW%d(%" PRIu32 ") ", bEndpoint, dLength);
/* Add buffer to buffer list and update index */
pTx = &(pMbl->pMbl[pMbl->inCurr]);
pTx->pBuffer = (uint8_t*)pData;
pTx->size = pTx->remaining = dLength;
pTx->transferred = pTx->buffered = 0;
/* Update input index */
if (pMbl->inCurr >= (pMbl->listSize-1)) pMbl->inCurr = 0;
else pMbl->inCurr ++;
if (pMbl->inCurr == pMbl->outCurr) pMbl->listState = MBL_FULL;
else pMbl->listState = 0;
/* Start sending when offset achieved */
if (MBL_NbBuffer(pMbl->inCurr, pMbl->outCurr, pMbl->listSize)
>= pMbl->offsetSize
&& pEndpoint->state == UDP_ENDPOINT_IDLE) {
TRACE_DEBUG_WP("StartT ");
/* Change state */
pEndpoint->state = UDP_ENDPOINT_SENDINGM;
while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY);
/* Send first packet */
UDP_MblWriteFifo(bEndpoint);
SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY);
/* If double buffering is enabled and there is remaining, continue */
if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1)
&& pMbl->pMbl[pMbl->outCurr].remaining) {
UDP_MblWriteFifo(bEndpoint);
}
/* Enable interrupt on endpoint */
UDP->UDP_IER = 1 << bEndpoint;
}
return USBD_STATUS_SUCCESS;
}
/**
* Reads incoming data on an USB endpoint This methods sets the transfer
* descriptor and activate the endpoint interrupt. The actual transfer is
* then carried out by the endpoint interrupt handler. The Read operation
* finishes either when the buffer is full, or a short packet (inferior to
* endpoint maximum size) is received.
*
* *The buffer must be kept allocated until the transfer is finished*.
* \param bEndpoint Endpoint number.
* \param pData Pointer to a data buffer.
* \param dLength Size of the data buffer in bytes.
* \return USBD_STATUS_SUCCESS if the read operation has been started;
* otherwise, the corresponding error code.
*/
static inline uint8_t UDP_Read(uint8_t bEndpoint,
void *pData,
uint32_t dLength)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer);
/* Return if the endpoint is not in IDLE state */
if (pEndpoint->state != UDP_ENDPOINT_IDLE) {
return USBD_STATUS_LOCKED;
}
/* Endpoint enters Receiving state */
pEndpoint->state = UDP_ENDPOINT_RECEIVING;
TRACE_DEBUG_WP("Read%d(%" PRIu32 ") ", bEndpoint, dLength);
/* int i;
for (i = 0; i < dLength; i++) {
if (!(i%16)) {
printf("\n\r");
}
printf("0x%x ", ((uint8_t*)pData)[i]);
}
printf("\n\r");
*/
/* Set the transfer descriptor */
pTransfer->pData = pData;
pTransfer->remaining = dLength;
pTransfer->buffered = 0;
pTransfer->transferred = 0;
/* Enable interrupt on endpoint */
UDP->UDP_IER = 1 << bEndpoint;
return USBD_STATUS_SUCCESS;
}
/*---------------------------------------------------------------------------
* Exported functions
*---------------------------------------------------------------------------*/
/**
* USBD (UDP) interrupt handler
* Manages device resume, suspend, end of bus reset.
* Forwards endpoint events to the appropriate handler.
*/
void USBD_IrqHandler(void)
{
uint32_t status;
int32_t eptnum = 0;
/* Enable peripheral ? */
//UDP_EnablePeripheralClock();
/* Get interrupt status
Some interrupts may get masked depending on the device state */
status = UDP->UDP_ISR;
status &= UDP->UDP_IMR;
TRACE_DEBUG("status; 0x%" PRIx32 , status);
if (USBD_GetState() < USBD_STATE_POWERED) {
status &= UDP_ICR_WAKEUP | UDP_ICR_RXRSM;
UDP->UDP_ICR = ~status;
}
/* Return immediately if there is no interrupt to service */
if (status == 0) {
TRACE_DEBUG_WP(".\n\r");
return;
}
/* Toggle USB LED if the device is active */
if (USBD_GetState() >= USBD_STATE_POWERED) {
//LED_Set(USBD_LEDUSB);
}
/* Service interrupts */
/** / Start Of Frame (SOF) */
//if (ISSET(dStatus, UDP_ISR_SOFINT)) {
//
// TRACE_DEBUG("SOF");
//
// // Invoke the SOF callback
// USB_StartOfFrameCallback(pUsb);
//
// // Acknowledge interrupt
// UDP->UDP_ICR = UDP_ICR_SOFINT;
// dStatus &= ~UDP_ISR_SOFINT;
//}
/* Resume (Wakeup) */
if ((status & (UDP_ISR_WAKEUP | UDP_ISR_RXRSM)) != 0) {
TRACE_INFO_WP("Res ");
/* Clear and disable resume interrupts */
UDP->UDP_ICR = UDP_ICR_WAKEUP | UDP_ICR_RXRSM | UDP_ICR_RXSUSP;
UDP->UDP_IDR = UDP_IDR_WAKEUP | UDP_IDR_RXRSM;
/* Do resome operations */
USBD_ResumeHandler();
}
/* Suspend
This interrupt is always treated last (hence the '==') */
if (status == UDP_ISR_RXSUSP) {
TRACE_INFO_WP("Susp ");
/* Enable wakeup */
UDP->UDP_IER = UDP_IER_WAKEUP | UDP_IER_RXRSM;
/* Acknowledge interrupt */
UDP->UDP_ICR = UDP_ICR_RXSUSP;
/* Do suspend operations */
USBD_SuspendHandler();
}
/* End of bus reset */
else if ((status & UDP_ISR_ENDBUSRES) != 0) {
TRACE_INFO_WP("EoBRes ");
#if defined(BOARD_USB_DFU) && defined(dfu)
if (g_dfu.past_manifest)
USBDFU_SwitchToApp();
#endif
/* Flush and enable the Suspend interrupt */
UDP->UDP_ICR = UDP_ICR_WAKEUP | UDP_ICR_RXRSM | UDP_ICR_RXSUSP;
UDP->UDP_IER = UDP_IER_RXSUSP;
/* Do RESET operations */
USBD_ResetHandler();
/* Acknowledge end of bus reset interrupt */
UDP->UDP_ICR = UDP_ICR_ENDBUSRES;
}
/* Endpoint interrupts */
else {
status &= ((1 << CHIP_USB_NUMENDPOINTS) - 1);
while (status != 0) {
/* Check if endpoint has a pending interrupt */
if ((status & (1 << eptnum)) != 0) {
UDP_EndpointHandler(eptnum);
status &= ~(1 << eptnum);
if (status != 0) {
TRACE_INFO_WP("\n\r - ");
}
}
eptnum++;
}
}
/* Toggle LED back to its previous state */
TRACE_DEBUG_WP("!");
TRACE_INFO_WP("\n\r");
if (USBD_GetState() >= USBD_STATE_POWERED) {
//LED_Clear(USBD_LEDUSB);
}
}
/**
* \brief Reset endpoints and disable them.
* -# Terminate transfer if there is any, with given status;
* -# Reset the endpoint & disable it.
* \param bmEPs Bitmap for endpoints to reset.
* \param bStatus Status passed to terminate transfer on endpoint.
* \param bKeepCfg 1 to keep old endpoint configuration.
* \note Use USBD_HAL_ConfigureEP() to configure and enable endpoint
if not keeping old configuration.
* \sa USBD_HAL_ConfigureEP().
*/
void USBD_HAL_ResetEPs(uint32_t bmEPs, uint8_t bStatus, uint8_t bKeepCfg)
{
Endpoint *pEndpoint;
uint32_t tmp = bmEPs & ((1<<CHIP_USB_NUMENDPOINTS)-1);
uint8_t ep;
uint32_t epBit, epCfg;
for (ep = 0, epBit = 1; ep < CHIP_USB_NUMENDPOINTS; ep ++) {
if (tmp & epBit) {
/* Disable ISR */
UDP->UDP_IDR = epBit;
/* Kill pending TXPKTREADY */
CLEAR_CSR(ep, UDP_CSR_TXPKTRDY);
/* Reset transfer information */
pEndpoint = &(endpoints[ep]);
/* Reset endpoint state */
pEndpoint->bank = 0;
/* Endpoint configure */
epCfg = UDP->UDP_CSR[ep];
/* Reset endpoint */
UDP->UDP_RST_EP |= epBit;
UDP->UDP_RST_EP &= ~epBit;
/* Restore configure */
if (bKeepCfg) {
//SET_CSR(ep, pEndpoint->CSR);
SET_CSR(ep, epCfg);
}
else {
//pEndpoint->CSR = 0;
pEndpoint->state = UDP_ENDPOINT_DISABLED;
}
/* Terminate transfer on this EP */
UDP_EndOfTransfer(ep, bStatus);
}
epBit <<= 1;
}
/* Reset EPs */
// UDP->UDP_RST_EP |= bmEPs;
// UDP->UDP_RST_EP &= ~bmEPs;
}
/**
* Cancel pending READ/WRITE
* \param bmEPs Bitmap for endpoints to reset.
* \note EP callback is invoked with USBD_STATUS_CANCELED.
*/
void USBD_HAL_CancelIo(uint32_t bmEPs)
{
uint32_t tmp = bmEPs & ((1<<CHIP_USB_NUMENDPOINTS)-1);
uint8_t ep;
uint32_t epBit;
for (ep = 0, epBit = 1; ep < CHIP_USB_NUMENDPOINTS; ep ++) {
if (tmp & epBit) {
/* Disable ISR */
UDP->UDP_IDR = epBit;
/* Kill pending TXPKTREADY */
CLEAR_CSR(ep, UDP_CSR_TXPKTRDY);
/* Terminate transfer on this EP */
UDP_EndOfTransfer(ep, USBD_STATUS_CANCELED);
}
epBit <<= 1;
}
}
/**
* Configures an endpoint according to its endpoint Descriptor.
* \param pDescriptor Pointer to an endpoint descriptor.
*/
uint8_t USBD_HAL_ConfigureEP(const USBEndpointDescriptor *pDescriptor)
{
Endpoint *pEndpoint;
uint8_t bEndpoint;
uint8_t bType;
uint8_t bEndpointDir;
/* NULL descriptor -> Control endpoint 0 in default */
if (pDescriptor == 0) {
bEndpoint = 0;
pEndpoint = &(endpoints[bEndpoint]);
bType= USBEndpointDescriptor_CONTROL;
bEndpointDir = 0;
pEndpoint->size = CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0);
}
/* Device descriptor -> Specific Control EP */
else if (pDescriptor->bDescriptorType == USBGenericDescriptor_DEVICE) {
bEndpoint = 0;
pEndpoint = &(endpoints[bEndpoint]);
bType = USBEndpointDescriptor_CONTROL;
bEndpointDir = 0;
pEndpoint->size = ((USBDeviceDescriptor *)pDescriptor)->bMaxPacketSize0;
}
/* Not endpoint descriptor, ERROR! */
else if (pDescriptor->bDescriptorType != USBGenericDescriptor_ENDPOINT) {
return 0xFF;
}
else {
bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor);
pEndpoint = &(endpoints[bEndpoint]);
bType = USBEndpointDescriptor_GetType(pDescriptor);
bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor);
pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor);
}
/* Abort the current transfer is the endpoint was configured and in
Write or Read state */
if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING)
|| (pEndpoint->state == UDP_ENDPOINT_SENDING)
|| (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM)
|| (pEndpoint->state == UDP_ENDPOINT_SENDINGM)) {
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_RESET);
}
pEndpoint->state = UDP_ENDPOINT_IDLE;
/* Reset Endpoint Fifos */
UDP->UDP_RST_EP |= (1 << bEndpoint);
UDP->UDP_RST_EP &= ~(1 << bEndpoint);
/* Configure endpoint */
SET_CSR(bEndpoint, (uint32_t)UDP_CSR_EPEDS
| (bType << 8) | (bEndpointDir << 10));
if (bType != USBEndpointDescriptor_CONTROL) {
}
else {
UDP->UDP_IER = (1 << bEndpoint);
}
TRACE_INFO_WP("CfgEp%d ", bEndpoint);
return bEndpoint;
}
/**
* Set callback for a USB endpoint for transfer (read/write).
*
* \param bEP Endpoint number.
* \param fCallback Optional callback function to invoke when the transfer is
* complete.
* \param pCbData Optional pointer to data to the callback function.
* \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED if endpoint is busy.
*/
uint8_t USBD_HAL_SetTransferCallback(uint8_t bEP,
TransferCallback fCallback,
void *pCbData)
{
Endpoint *pEndpoint = &(endpoints[bEP]);
TransferHeader *pTransfer = (TransferHeader*)&(pEndpoint->transfer);
/* Check that the endpoint is not transferring */
if (pEndpoint->state > UDP_ENDPOINT_IDLE) {
return USBD_STATUS_LOCKED;
}
TRACE_DEBUG_WP("sXfrCb ");
/* Setup the transfer callback and extension data */
pTransfer->fCallback = (void*)fCallback;
pTransfer->pArgument = pCbData;
return USBD_STATUS_SUCCESS;
}
/**
* Configure an endpoint to use multi-buffer-list transfer mode.
* The buffers can be added by _Read/_Write function.
* \param pMbList Pointer to a multi-buffer list used, NULL to disable MBL.
* \param mblSize Multi-buffer list size (number of buffers can be queued)
* \param startOffset When number of buffer achieve this offset transfer start
*/
uint8_t USBD_HAL_SetupMblTransfer( uint8_t bEndpoint,
USBDTransferBuffer* pMbList,
uint16_t mblSize,
uint16_t startOffset)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
MblTransfer *pXfr = (MblTransfer*)&(pEndpoint->transfer);
uint16_t i;
/* Check that the endpoint is not transferring */
if (pEndpoint->state > UDP_ENDPOINT_IDLE) {
return USBD_STATUS_LOCKED;
}
TRACE_DEBUG_WP("sMblXfr ");
/* Enable Multi-Buffer Transfer List */
if (pMbList) {
/* Reset list items */
for (i = 0; i < mblSize; i --) {
pMbList[i].pBuffer = NULL;
pMbList[i].size = 0;
pMbList[i].transferred = 0;
pMbList[i].buffered = 0;
pMbList[i].remaining = 0;
}
/* Setup transfer */
pXfr->transType = 1;
pXfr->listState = 0; /* OK */
pXfr->listSize = mblSize;
pXfr->pMbl = pMbList;
pXfr->outCurr = pXfr->outLast = 0;
pXfr->inCurr = 0;
pXfr->offsetSize = startOffset;
}
/* Disable Multi-Buffer Transfer */
else {
pXfr->transType = 0;
pXfr->pMbl = NULL;
pXfr->listSize = 0;
pXfr->offsetSize = 1;
}
return USBD_STATUS_SUCCESS;
}
/**
* Sends data through a USB endpoint. Sets up the transfer descriptor,
* writes one or two data payloads (depending on the number of FIFO bank
* for the endpoint) and then starts the actual transfer. The operation is
* complete when all the data has been sent.
*
* *If the size of the buffer is greater than the size of the endpoint
* (or twice the size if the endpoint has two FIFO banks), then the buffer
* must be kept allocated until the transfer is finished*. This means that
* it is not possible to declare it on the stack (i.e. as a local variable
* of a function which returns after starting a transfer).
*
* \param bEndpoint Endpoint number.
* \param pData Pointer to a buffer with the data to send.
* \param dLength Size of the data buffer.
* \return USBD_STATUS_SUCCESS if the transfer has been started;
* otherwise, the corresponding error status code.
*/
uint8_t USBD_HAL_Write( uint8_t bEndpoint,
const void *pData,
uint32_t dLength)
{
if (endpoints[bEndpoint].transfer.transHdr.transType)
return UDP_AddWr(bEndpoint, pData, dLength);
else
return UDP_Write(bEndpoint, pData, dLength);
}
/**
* Reads incoming data on an USB endpoint This methods sets the transfer
* descriptor and activate the endpoint interrupt. The actual transfer is
* then carried out by the endpoint interrupt handler. The Read operation
* finishes either when the buffer is full, or a short packet (inferior to
* endpoint maximum size) is received.
*
* *The buffer must be kept allocated until the transfer is finished*.
* \param bEndpoint Endpoint number.
* \param pData Pointer to a data buffer.
* \param dLength Size of the data buffer in bytes.
* \return USBD_STATUS_SUCCESS if the read operation has been started;
* otherwise, the corresponding error code.
*/
uint8_t USBD_HAL_Read(uint8_t bEndpoint,
void *pData,
uint32_t dLength)
{
if (endpoints[bEndpoint].transfer.transHdr.transType)
return USBD_STATUS_SW_NOT_SUPPORTED;
else
return UDP_Read(bEndpoint, pData, dLength);
}
/**
* \brief Enable Pull-up, connect.
*
* -# Enable HW access if needed
* -# Enable Pull-Up
* -# Disable HW access if needed
*/
void USBD_HAL_Connect(void)
{
uint8_t dis = UDP_EnablePeripheralClock();
UDP->UDP_TXVC |= UDP_TXVC_PUON;
if (dis) UDP_DisablePeripheralClock();
}
/**
* \brief Disable Pull-up, disconnect.
*
* -# Enable HW access if needed
* -# Disable PULL-Up
* -# Disable HW access if needed
*/
void USBD_HAL_Disconnect(void)
{
uint8_t dis = UDP_EnablePeripheralClock();
UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_PUON;
if (dis) UDP_DisablePeripheralClock();
}
/**
* Starts a remote wake-up procedure.
*/
void USBD_HAL_RemoteWakeUp(void)
{
UDP_EnablePeripheralClock();
UDP_EnableUsbClock();
UDP_EnableTransceiver();
TRACE_INFO_WP("RWUp ");
// Activates a remote wakeup (edge on ESR), then clear ESR
UDP->UDP_GLB_STAT |= UDP_GLB_STAT_ESR;
UDP->UDP_GLB_STAT &= ~(uint32_t)UDP_GLB_STAT_ESR;
}
/**
* Sets the device address to the given value.
* \param address New device address.
*/
void USBD_HAL_SetAddress(uint8_t address)
{
/* Set address */
UDP->UDP_FADDR = UDP_FADDR_FEN | (address & UDP_FADDR_FADD_Msk);
/* If the address is 0, the device returns to the Default state */
if (address == 0) UDP->UDP_GLB_STAT = 0;
/* If the address is non-zero, the device enters the Address state */
else UDP->UDP_GLB_STAT = UDP_GLB_STAT_FADDEN;
}
/**
* Sets the current device configuration.
* \param cfgnum - Configuration number to set.
*/
void USBD_HAL_SetConfiguration(uint8_t cfgnum)
{
/* If the configuration number if non-zero, the device enters the
Configured state */
if (cfgnum != 0) UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG;
/* If the configuration number is zero, the device goes back to the Address
state */
else {
UDP->UDP_GLB_STAT = UDP_FADDR_FEN;
}
}
/**
* Initializes the USB HW Access driver.
*/
void USBD_HAL_Init(void)
{
TRACE_DEBUG("%s\n\r", "USBD_HAL_Init");
/* Must before USB & TXVC access! */
UDP_EnablePeripheralClock();
/* Reset & disable endpoints */
USBD_HAL_ResetEPs(0xFFFFFFFF, USBD_STATUS_RESET, 0);
/* Configure the pull-up on D+ and disconnect it */
UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_PUON;
UDP_EnableUsbClock();
UDP->UDP_IDR = 0xFE;
UDP->UDP_IER = UDP_IER_WAKEUP;
}
/**
* Causes the given endpoint to acknowledge the next packet it receives
* with a STALL handshake except setup request.
* \param bEP Endpoint number.
* \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED.
*/
uint8_t USBD_HAL_Stall(uint8_t bEP)
{
Endpoint *pEndpoint = &(endpoints[bEP]);
/* Check that endpoint is in Idle state */
if (pEndpoint->state != UDP_ENDPOINT_IDLE) {
TRACE_WARNING("UDP_Stall: EP%d locked\n\r", bEP);
return USBD_STATUS_LOCKED;
}
/* STALL endpoint */
SET_CSR(bEP, UDP_CSR_FORCESTALL);
TRACE_DEBUG_WP("Stall%d ", bEP);
return USBD_STATUS_SUCCESS;
}
/**
* Sets/Clear/Get the HALT state on the endpoint.
* In HALT state, the endpoint should keep stalling any packet.
* \param bEndpoint Endpoint number.
* \param ctl Control code CLR/HALT/READ.
* 0: Clear HALT state;
* 1: Set HALT state;
* .: Return HALT status.
* \return USBD_STATUS_INVALID_PARAMETER if endpoint not exist,
* otherwise endpoint halt status.
*/
uint8_t USBD_HAL_Halt(uint8_t bEndpoint, uint8_t ctl)
{
Endpoint *pEndpoint = &(endpoints[bEndpoint]);
uint8_t status = 0;
/* SET Halt */
if (ctl == 1) {
/* Check that endpoint is enabled and not already in Halt state */
if ((pEndpoint->state != UDP_ENDPOINT_DISABLED)
&& (pEndpoint->state != UDP_ENDPOINT_HALTED)) {
TRACE_DEBUG_WP("Halt%d ", bEndpoint);
/* Abort the current transfer if necessary */
UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED);
/* Put endpoint into Halt state */
SET_CSR(bEndpoint, UDP_CSR_FORCESTALL);
pEndpoint->state = UDP_ENDPOINT_HALTED;
/* Enable the endpoint interrupt */
UDP->UDP_IER = 1 << bEndpoint;
}
}
/* CLEAR Halt */
else if (ctl == 0) {
/* Check if the endpoint is halted */
//if (pEndpoint->state != UDP_ENDPOINT_DISABLED) {
if (pEndpoint->state == UDP_ENDPOINT_HALTED) {
TRACE_DEBUG_WP("Unhalt%d ", bEndpoint);
/* Return endpoint to Idle state */
pEndpoint->state = UDP_ENDPOINT_IDLE;
/* Clear FORCESTALL flag */
CLEAR_CSR(bEndpoint, UDP_CSR_FORCESTALL);
/* Reset Endpoint Fifos, beware this is a 2 steps operation */
UDP->UDP_RST_EP |= 1 << bEndpoint;
UDP->UDP_RST_EP &= ~(1 << bEndpoint);
}
}
/* Return Halt status */
if (pEndpoint->state == UDP_ENDPOINT_HALTED) {
status = 1;
}
return( status );
}
/**
* Indicates if the device is running in high or full-speed. Always returns 0
* since UDP does not support high-speed mode.
*/
uint8_t USBD_HAL_IsHighSpeed(void)
{
return 0;
}
/**
* Suspend USB Device HW Interface
*
* -# Disable transceiver
* -# Disable USB Clock
* -# Disable USB Peripheral
*/
void USBD_HAL_Suspend(void)
{
/* The device enters the Suspended state */
UDP_DisableTransceiver();
UDP_DisableUsbClock();
UDP_DisablePeripheralClock();
}
/**
* Activate USB Device HW Interface
* -# Enable USB Peripheral
* -# Enable USB Clock
* -# Enable transceiver
*/
void USBD_HAL_Activate(void)
{
UDP_EnablePeripheralClock();
UDP_EnableUsbClock();
UDP_EnableTransceiver();
}
/**@}*/