/* ---------------------------------------------------------------------------- | |
* 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 "USBD API Methods". | |
*/ | |
/** \addtogroup usbd_interface | |
*@{ | |
*/ | |
/*--------------------------------------------------------------------------- | |
* Headers | |
*---------------------------------------------------------------------------*/ | |
#include "USBD.h" | |
#include "USBD_HAL.h" | |
#include <USBLib_Trace.h> | |
/*--------------------------------------------------------------------------- | |
* Definitions | |
*---------------------------------------------------------------------------*/ | |
/*--------------------------------------------------------------------------- | |
* Internal variables | |
*---------------------------------------------------------------------------*/ | |
/** Device current state. */ | |
static uint8_t deviceState; | |
/** Indicates the previous device state */ | |
static uint8_t previousDeviceState; | |
/*--------------------------------------------------------------------------- | |
* Internal Functions | |
*---------------------------------------------------------------------------*/ | |
/*--------------------------------------------------------------------------- | |
* Exported functions | |
*---------------------------------------------------------------------------*/ | |
/*--------------------------------------------------------------------------- | |
* USBD: Event handlers | |
*---------------------------------------------------------------------------*/ | |
/** | |
* Handle the USB suspend event, should be invoked whenever | |
* HW reports a suspend signal. | |
*/ | |
void USBD_SuspendHandler(void) | |
{ | |
/* Don't do anything if the device is already suspended */ | |
if (deviceState != USBD_STATE_SUSPENDED) { | |
/* Switch to the Suspended state */ | |
previousDeviceState = deviceState; | |
deviceState = USBD_STATE_SUSPENDED; | |
/* Suspend HW interface */ | |
USBD_HAL_Suspend(); | |
/* Invoke the User Suspended callback (Suspend System?) */ | |
if (NULL != USBDCallbacks_Suspended) | |
USBDCallbacks_Suspended(); | |
} | |
} | |
/** | |
* Handle the USB resume event, should be invoked whenever | |
* HW reports a resume signal. | |
*/ | |
void USBD_ResumeHandler(void) | |
{ | |
/* Don't do anything if the device was not suspended */ | |
if (deviceState == USBD_STATE_SUSPENDED) { | |
/* Active the device */ | |
USBD_HAL_Activate(); | |
deviceState = previousDeviceState; | |
if (deviceState >= USBD_STATE_DEFAULT) { | |
/* Invoke the Resume callback */ | |
if (NULL != USBDCallbacks_Resumed) | |
USBDCallbacks_Resumed(); | |
} | |
} | |
} | |
/** | |
* Handle the USB reset event, should be invoked whenever | |
* HW found USB reset signal on bus, which usually is called | |
* "end of bus reset" status. | |
*/ | |
void USBD_ResetHandler() | |
{ | |
/* The device enters the Default state */ | |
deviceState = USBD_STATE_DEFAULT; | |
/* Active the USB HW */ | |
USBD_HAL_Activate(); | |
/* Only EP0 enabled */ | |
USBD_HAL_ResetEPs(0xFFFFFFFF, USBD_STATUS_RESET, 0); | |
USBD_ConfigureEndpoint(0); | |
/* Invoke the Reset callback */ | |
if (NULL != USBDCallbacks_Reset) | |
USBDCallbacks_Reset(); | |
} | |
/** | |
* Handle the USB setup package received, should be invoked | |
* when an endpoint got a setup package as request. | |
* \param bEndpoint Endpoint number. | |
* \param pRequest Pointer to content of request. | |
*/ | |
void USBD_RequestHandler(uint8_t bEndpoint, | |
const USBGenericRequest* pRequest) | |
{ | |
if (bEndpoint != 0) { | |
TRACE_WARNING("EP%d request not supported, default EP only", | |
bEndpoint); | |
} | |
else if (NULL != USBDCallbacks_RequestReceived) { | |
USBDCallbacks_RequestReceived(pRequest); | |
} | |
} | |
/*--------------------------------------------------------------------------- | |
* USBD: Library interface | |
*---------------------------------------------------------------------------*/ | |
/** | |
* Configures an endpoint according to its Endpoint Descriptor. | |
* \param pDescriptor Pointer to an Endpoint descriptor. | |
*/ | |
void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) | |
{ | |
USBD_HAL_ConfigureEP(pDescriptor); | |
} | |
/** | |
* 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. | |
* \param fCallback Optional callback function to invoke when the transfer is | |
* complete. | |
* \param pArgument Optional argument to the callback function. | |
* \return USBD_STATUS_SUCCESS if the transfer has been started; | |
* otherwise, the corresponding error status code. | |
*/ | |
uint8_t USBD_Write( uint8_t bEndpoint, | |
const void *pData, | |
uint32_t dLength, | |
TransferCallback fCallback, | |
void *pArgument ) | |
{ | |
USBD_HAL_SetTransferCallback(bEndpoint, fCallback, pArgument); | |
return USBD_HAL_Write(bEndpoint, pData, dLength); | |
} | |
#if 0 | |
/** | |
* Sends data frames 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 data has been sent. | |
* | |
* *If the size of the frame 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 frame 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 pMbl Pointer to a frame (USBDTransferBuffer) list that describes | |
* the buffer list to send. | |
* \param wListSize Size of the frame list. | |
* \param bCircList Circle the list. | |
* \param wStartNdx For circled list only, the first buffer index to transfer. | |
* \param fCallback Optional callback function to invoke when the transfer is | |
* complete. | |
* \param pArgument Optional argument to the callback function. | |
* \return USBD_STATUS_SUCCESS if the transfer has been started; | |
* otherwise, the corresponding error status code. | |
* \see USBDTransferBuffer, MblTransferCallback, USBD_MblReuse | |
*/ | |
uint8_t USBD_MblWrite( uint8_t bEndpoint, | |
void *pMbl, | |
uint16_t wListSize, | |
uint8_t bCircList, | |
uint16_t wStartNdx, | |
MblTransferCallback fCallback, | |
void *pArgument ) | |
{ | |
Endpoint *pEndpoint = &(endpoints[bEndpoint]); | |
MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); | |
uint16_t i; | |
/* EP0 is not suitable for Mbl */ | |
if (bEndpoint == 0) { | |
return USBD_STATUS_INVALID_PARAMETER; | |
} | |
/* Check that the endpoint is in Idle state */ | |
if (pEndpoint->state != UDP_ENDPOINT_IDLE) { | |
return USBD_STATUS_LOCKED; | |
} | |
pEndpoint->state = UDP_ENDPOINT_SENDINGM; | |
TRACE_DEBUG_WP("WriteM%d(0x%x,%d) ", bEndpoint, pMbl, wListSize); | |
/* Start from first if not circled list */ | |
if (!bCircList) wStartNdx = 0; | |
/* Setup the transfer descriptor */ | |
pTransfer->pMbl = (USBDTransferBuffer*)pMbl; | |
pTransfer->listSize = wListSize; | |
pTransfer->fCallback = fCallback; | |
pTransfer->pArgument = pArgument; | |
pTransfer->currBuffer = wStartNdx; | |
pTransfer->freedBuffer = 0; | |
pTransfer->pLastLoaded = &(((USBDTransferBuffer*)pMbl)[wStartNdx]); | |
pTransfer->circList = bCircList; | |
pTransfer->allUsed = 0; | |
/* Clear all buffer */ | |
for (i = 0; i < wListSize; i ++) { | |
pTransfer->pMbl[i].transferred = 0; | |
pTransfer->pMbl[i].buffered = 0; | |
pTransfer->pMbl[i].remaining = pTransfer->pMbl[i].size; | |
} | |
/* Send the first packet */ | |
while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY); | |
UDP_MblWriteFifo(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->pMbl[pTransfer->currBuffer].remaining > 0)) { | |
UDP_MblWriteFifo(bEndpoint); | |
} | |
/* Enable interrupt on endpoint */ | |
UDP->UDP_IER = 1 << bEndpoint; | |
return USBD_STATUS_SUCCESS; | |
} | |
#endif | |
/** | |
* 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. | |
* \param fCallback Optional end-of-transfer callback function. | |
* \param pArgument Optional argument to the callback function. | |
* \return USBD_STATUS_SUCCESS if the read operation has been started; | |
* otherwise, the corresponding error code. | |
*/ | |
uint8_t USBD_Read(uint8_t bEndpoint, | |
void *pData, | |
uint32_t dLength, | |
TransferCallback fCallback, | |
void *pArgument) | |
{ | |
USBD_HAL_SetTransferCallback(bEndpoint, fCallback, pArgument); | |
return USBD_HAL_Read(bEndpoint, pData, dLength); | |
} | |
#if 0 | |
/** | |
* Reuse first used/released buffer with new buffer address and size to be used | |
* in transfer again. Only valid when frame list is ringed. Can be used for | |
* both read & write. | |
* \param bEndpoint Endpoint number. | |
* \param pNewBuffer Pointer to new buffer with data to send (0 to keep last). | |
* \param wNewSize Size of the data buffer | |
*/ | |
uint8_t USBD_MblReuse( uint8_t bEndpoint, | |
uint8_t *pNewBuffer, | |
uint16_t wNewSize ) | |
{ | |
Endpoint *pEndpoint = &(endpoints[bEndpoint]); | |
MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); | |
USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->freedBuffer]); | |
TRACE_DEBUG_WP("MblReuse(%d), st%x, circ%d\n\r", | |
bEndpoint, pEndpoint->state, pTransfer->circList); | |
/* Only for Multi-buffer-circle list */ | |
if (bEndpoint != 0 | |
&& (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM | |
|| pEndpoint->state == UDP_ENDPOINT_SENDINGM) | |
&& pTransfer->circList) { | |
} | |
else { | |
return USBD_STATUS_WRONG_STATE; | |
} | |
/* Check if there is freed buffer */ | |
if (pTransfer->freedBuffer == pTransfer->currBuffer | |
&& !pTransfer->allUsed) { | |
return USBD_STATUS_LOCKED; | |
} | |
/* Update transfer information */ | |
if ((++ pTransfer->freedBuffer) == pTransfer->listSize) | |
pTransfer->freedBuffer = 0; | |
if (pNewBuffer) { | |
pBi->pBuffer = pNewBuffer; | |
pBi->size = wNewSize; | |
} | |
pBi->buffered = 0; | |
pBi->transferred = 0; | |
pBi->remaining = pBi->size; | |
/* At least one buffer is not processed */ | |
pTransfer->allUsed = 0; | |
return USBD_STATUS_SUCCESS; | |
} | |
#endif | |
/** | |
* Sets the HALT feature on the given endpoint (if not already in this state). | |
* \param bEndpoint Endpoint number. | |
*/ | |
void USBD_Halt(uint8_t bEndpoint) | |
{ | |
USBD_HAL_Halt(bEndpoint, 1); | |
} | |
/** | |
* Clears the Halt feature on the given endpoint. | |
* \param bEndpoint Index of endpoint | |
*/ | |
void USBD_Unhalt(uint8_t bEndpoint) | |
{ | |
USBD_HAL_Halt(bEndpoint, 0); | |
} | |
/** | |
* Returns the current Halt status of an endpoint. | |
* \param bEndpoint Index of endpoint | |
* \return 1 if the endpoint is currently halted; otherwise 0 | |
*/ | |
uint8_t USBD_IsHalted(uint8_t bEndpoint) | |
{ | |
return USBD_HAL_Halt(bEndpoint, 0xFF); | |
} | |
/** | |
* 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_IsHighSpeed(void) | |
{ | |
return USBD_HAL_IsHighSpeed(); | |
} | |
/** | |
* Causes the given endpoint to acknowledge the next packet it receives | |
* with a STALL handshake. | |
* \param bEndpoint Endpoint number. | |
* \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED. | |
*/ | |
uint8_t USBD_Stall(uint8_t bEndpoint) | |
{ | |
return USBD_HAL_Stall(bEndpoint); | |
} | |
/** | |
* Sets the device address to the given value. | |
* \param address New device address. | |
*/ | |
void USBD_SetAddress(uint8_t address) | |
{ | |
TRACE_INFO_WP("SetAddr(%d) ", address); | |
USBD_HAL_SetAddress(address); | |
if (address == 0) deviceState = USBD_STATE_DEFAULT; | |
else deviceState = USBD_STATE_ADDRESS; | |
} | |
/** | |
* Sets the current device configuration. | |
* \param cfgnum - Configuration number to set. | |
*/ | |
void USBD_SetConfiguration(uint8_t cfgnum) | |
{ | |
TRACE_INFO_WP("SetCfg(%d) ", cfgnum); | |
USBD_HAL_SetConfiguration(cfgnum); | |
if (cfgnum != 0) { | |
deviceState = USBD_STATE_CONFIGURED; | |
} | |
else { | |
deviceState = USBD_STATE_ADDRESS; | |
/* Reset all endpoints but Control 0 */ | |
USBD_HAL_ResetEPs(0xFFFFFFFE, USBD_STATUS_RESET, 0); | |
} | |
} | |
/*--------------------------------------------------------------------------- | |
* USBD: Library API | |
*---------------------------------------------------------------------------*/ | |
/** | |
* Starts a remote wake-up procedure. | |
*/ | |
void USBD_RemoteWakeUp(void) | |
{ | |
/* Device is NOT suspended */ | |
if (deviceState != USBD_STATE_SUSPENDED) { | |
TRACE_INFO("USBD_RemoteWakeUp: Device is not suspended\n\r"); | |
return; | |
} | |
USBD_HAL_Activate(); | |
USBD_HAL_RemoteWakeUp(); | |
} | |
/** | |
* Connects the pull-up on the D+ line of the USB. | |
*/ | |
void USBD_Connect(void) | |
{ | |
USBD_HAL_Connect(); | |
} | |
/** | |
* Disconnects the pull-up from the D+ line of the USB. | |
*/ | |
void USBD_Disconnect(void) | |
{ | |
USBD_HAL_Disconnect(); | |
/* Device returns to the Powered state */ | |
if (deviceState > USBD_STATE_POWERED) { | |
deviceState = USBD_STATE_POWERED; | |
} | |
if (previousDeviceState > USBD_STATE_POWERED) { | |
previousDeviceState = USBD_STATE_POWERED; | |
} | |
} | |
/** | |
* Initializes the USB driver. | |
*/ | |
void USBD_Init(void) | |
{ | |
TRACE_INFO_WP("USBD_Init\n\r"); | |
/* HW Layer Initialize */ | |
USBD_HAL_Init(); | |
/* Device is in the Attached state */ | |
deviceState = USBD_STATE_SUSPENDED; | |
previousDeviceState = USBD_STATE_POWERED; | |
/* Upper Layer Initialize */ | |
if (NULL != USBDCallbacks_Initialized) | |
USBDCallbacks_Initialized(); | |
} | |
/** | |
* Returns the current state of the USB device. | |
* \return Device current state. | |
*/ | |
uint8_t USBD_GetState(void) | |
{ | |
return deviceState; | |
} | |
/** | |
* Certification test for High Speed device. | |
* \param bIndex Test to be done | |
*/ | |
void USBD_Test(uint8_t bIndex) | |
{ | |
USBD_HAL_Test(bIndex); | |
} | |
/**@}*/ |