at91work/at91lib/usb/device/core/USBD_OTGHS.c

1678 lines
59 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.
* ----------------------------------------------------------------------------
*/
/*!
Functions for OTGHS peripheral usage.
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#ifdef CHIP_OTGHS
#include "common.h"
#include "trace.h"
#include "usb.h"
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#define NUM_IT_MAX (AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_EPT_NBR_MAX)
#define NUM_IT_MAX_DMA ((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4)
#define SHIFT_DMA 24
#define SHIFT_INTERUPT 12
#define DMA
//------------------------------------------------------------------------------
// Structures
//------------------------------------------------------------------------------
// \brief Endpoint states
typedef enum {
endpointStateDisabled,
endpointStateIdle,
endpointStateWrite,
endpointStateRead,
endpointStateHalted
} EndpointState_t;
//------------------------------------------------------------------------------
// Macros
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Internal Functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// \brief Returns a pointer to the OTGHS controller interface used by an USB
// driver
//
// The pointer is cast to the correct type (AT91PS_OTGHS).
// \param pUsb Pointer to a S_usb instance
// \return Pointer to the USB controller interface
// \see S_usb
//------------------------------------------------------------------------------
static AT91PS_OTGHS OTGHS_GetDriverInterface(const S_usb *pUsb)
{
return (AT91PS_OTGHS) pUsb->pDriver->pInterface;
}
//------------------------------------------------------------------------------
// \fn OTGHS_GetInterfaceEPT
// \brief Returns OTGHS endpoint FIFO interface from S_usb structure
//------------------------------------------------------------------------------
static AT91PS_OTGHS_EPTFIFO OTGHS_GetInterfaceEPT(const S_usb *pUsb)
{
return (AT91PS_OTGHS_EPTFIFO) pUsb->pDriver->pEndpointFIFO;
}
//------------------------------------------------------------------------------
// \brief Enables the peripheral clock of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_EnableMCK(const S_usb *pUsb)
{
}
//------------------------------------------------------------------------------
// \brief Disables the peripheral clock of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_DisableMCK(const S_usb *pUsb)
{
}
//------------------------------------------------------------------------------
// \brief Enables the 48MHz clock of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_EnableOTGHSCK(const S_usb *pUsb)
{
}
//------------------------------------------------------------------------------
// \brief Disables the 48MHz clock of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_DisableOTGHSCK(const S_usb *pUsb)
{
}
//------------------------------------------------------------------------------
// \brief Enables the transceiver of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_EnableTransceiver(const S_usb *pUsb)
{
SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_OTGPADE);
}
//------------------------------------------------------------------------------
// \brief Disables the transceiver of the USB controller associated with
// the specified USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_DisableTransceiver(const S_usb *pUsb)
{
CLEAR(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_OTGPADE);
}
//------------------------------------------------------------------------------
// \brief Invokes the callback associated with a finished transfer on an
// endpoint
// \param pEndpoint Pointer to a S_usb_endpoint instance
// \param bStatus Status code returned by the transfer operation
// \see Status codes
// \see S_usb_endpoint
//------------------------------------------------------------------------------
static void OTGHS_EndOfTransfer(S_usb_endpoint *pEndpoint,
char bStatus)
{
if ((pEndpoint->dState == endpointStateWrite)
|| (pEndpoint->dState == endpointStateRead)) {
TRACE_DEBUG_WP("E");
// Endpoint returns in Idle state
pEndpoint->dState = endpointStateIdle;
// Invoke callback is present
if (pEndpoint->fCallback != 0) {
pEndpoint->fCallback((unsigned int) pEndpoint->pArgument,
(unsigned int) bStatus,
pEndpoint->dBytesTransferred,
pEndpoint->dBytesRemaining
+ pEndpoint->dBytesBuffered);
}
}
}
//------------------------------------------------------------------------------
// \brief Transfers a data payload from the current tranfer buffer to the
// endpoint FIFO.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \return Number of bytes transferred
// \see S_usb
//------------------------------------------------------------------------------
static unsigned int OTGHS_WritePayload(const S_usb *pUsb,
unsigned char bEndpoint)
{
AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
char *pfifo;
unsigned int dBytes;
unsigned int dCtr;
pfifo = (char*)&(pInterfaceEPT->OTGHS_READEPT0[bEndpoint*16384]);
// Get the number of bytes to send
dBytes = min(pEndpoint->wMaxPacketSize, pEndpoint->dBytesRemaining);
// Transfer one packet in the FIFO buffer
for (dCtr = 0; dCtr < dBytes; dCtr++) {
pfifo[dCtr] = *(pEndpoint->pData);
pEndpoint->pData++;
}
pEndpoint->dBytesBuffered += dBytes;
pEndpoint->dBytesRemaining -= dBytes;
return dBytes;
}
//----------------------------------------------------------------------------
// \brief Transfers a data payload from an endpoint FIFO to the current
// transfer buffer.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \param wPacketSize Size of received data packet
// \return Number of bytes transferred
// \see S_usb
//------------------------------------------------------------------------------
static unsigned int OTGHS_GetPayload(const S_usb *pUsb,
unsigned char bEndpoint,
unsigned short wPacketSize)
{
AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
char *pfifo;
unsigned int dBytes;
unsigned int dCtr;
pfifo = (char*)&(pInterfaceEPT->OTGHS_READEPT0[bEndpoint*16384]);
// Get number of bytes to retrieve
dBytes = min(pEndpoint->dBytesRemaining, wPacketSize);
// Retrieve packet
for (dCtr = 0; dCtr < dBytes; dCtr++) {
*(pEndpoint->pData) = pfifo[dCtr];
pEndpoint->pData++;
}
pEndpoint->dBytesRemaining -= dBytes;
pEndpoint->dBytesTransferred += dBytes;
pEndpoint->dBytesBuffered += wPacketSize - dBytes;
return dBytes;
}
//------------------------------------------------------------------------------
// \brief Transfers a received SETUP packet from endpoint 0 FIFO to the
// S_usb_request structure of an USB driver
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_GetSetup(const S_usb *pUsb)
{
unsigned int *pData = (unsigned int *) USB_GetSetup(pUsb);
AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb);
pData[0] = pInterfaceEPT->OTGHS_READEPT0[0];
pData[1] = pInterfaceEPT->OTGHS_READEPT0[0];
}
//------------------------------------------------------------------------------
// \brief This function reset all endpoint transfer descriptors
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_ResetEndpoints(const S_usb *pUsb)
{
S_usb_endpoint *pEndpoint;
unsigned char bEndpoint;
// Reset the transfer descriptor of every endpoint
for (bEndpoint = 0; bEndpoint < pUsb->dNumEndpoints; bEndpoint++) {
pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
// Reset endpoint transfer descriptor
pEndpoint->pData = 0;
pEndpoint->dBytesRemaining = 0;
pEndpoint->dBytesTransferred = 0;
pEndpoint->dBytesBuffered = 0;
pEndpoint->fCallback = 0;
pEndpoint->pArgument = 0;
// Configure endpoint characteristics
pEndpoint->dState = endpointStateDisabled;
}
}
//------------------------------------------------------------------------------
// \brief Disable all endpoints (except control endpoint 0), aborting current
// transfers if necessary.
// \param pUsb Pointer to a S_usb instance
//------------------------------------------------------------------------------
static void OTGHS_DisableEndpoints(const S_usb *pUsb)
{
S_usb_endpoint *pEndpoint;
unsigned char bEndpoint;
// Foreach endpoint, if it is enabled, disable it and invoke the callback
// Control endpoint 0 is not disabled
for (bEndpoint = 1; bEndpoint < pUsb->dNumEndpoints; bEndpoint++) {
pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_RESET);
pEndpoint->dState = endpointStateDisabled;
}
}
//------------------------------------------------------------------------------
// \brief Endpoint interrupt handler.
//
// Handle IN/OUT transfers, received SETUP packets and STALLing
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_EndpointHandler(const S_usb *pUsb, unsigned char bEndpoint)
{
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
unsigned int dStatus = pInterface->OTGHS_DEVEPTCSR[bEndpoint];
unsigned short wPacketSize;
TRACE_DEBUG_WP("Ept%d, 0x%X ", bEndpoint, dStatus);
// Handle interrupts
// IN packet sent
if((ISSET(pInterface->OTGHS_DEVEPTCMR[bEndpoint], AT91C_OTGHS_TXINI))
&& (ISSET(dStatus, AT91C_OTGHS_TXINI ))) {
TRACE_DEBUG_WP("Wr ");
if (pEndpoint->dBytesBuffered > 0) {
TRACE_DEBUG_WP("%d ", pEndpoint->dBytesBuffered);
pEndpoint->dBytesTransferred += pEndpoint->dBytesBuffered;
pEndpoint->dBytesBuffered = 0;
}
if ((!pEndpoint->isDataSent) || (pEndpoint->dBytesRemaining > 0)) {
OTGHS_WritePayload(pUsb, bEndpoint);
pEndpoint->isDataSent = true;
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_TXINI;
// For a non-control endpoint, the FIFOCON bit must be cleared
// to start the transfer
if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])
!= AT91C_OTGHS_EPT_TYPE_CTL_EPT) {
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_FIFOCON;
}
}
else {
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_TXINI;
// Disable interrupt if this is not a control endpoint
if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])
!= AT91C_OTGHS_EPT_TYPE_CTL_EPT) {
pInterface->OTGHS_DEVIDR = 1<<SHIFT_INTERUPT<<bEndpoint;
}
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS);
}
}
// OUT packet received
if(ISSET(dStatus, AT91C_OTGHS_RXOUT)) {
TRACE_DEBUG_WP("Rd ");
// Check that the endpoint is in Read state
if (pEndpoint->dState != endpointStateRead) {
// Endpoint is NOT in Read state
if (ISCLEARED(pInterface->OTGHS_DEVEPTCFG[bEndpoint], AT91C_OTGHS_EPT_TYPE)
&& ISCLEARED(dStatus, (0x7FF<<20))) { // byte count
// Control endpoint, 0 bytes received
// Acknowledge the data and finish the current transfer
TRACE_DEBUG_WP("Ack ");
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT;
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS);
}
else if (ISSET(dStatus, AT91C_OTGHS_STALL)) {
// Non-control endpoint
// Discard stalled data
TRACE_DEBUG_WP("Disc ");
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT;
}
else {
// Non-control endpoint
// Nak data
TRACE_DEBUG_WP("Nak ");
pInterface->OTGHS_DEVIDR = 1<<SHIFT_INTERUPT<<bEndpoint;
}
}
else {
// Endpoint is in Read state
// Retrieve data and store it into the current transfer buffer
wPacketSize = (unsigned short) ((dStatus >> 20) & 0x7FF);
TRACE_DEBUG_WP("%d ", wPacketSize);
OTGHS_GetPayload(pUsb, bEndpoint, wPacketSize);
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT;
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_FIFOCON;
if ((pEndpoint->dBytesRemaining == 0)
|| (wPacketSize < pEndpoint->wMaxPacketSize)) {
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_RXOUT;
// Disable interrupt if this is not a control endpoint
if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])
!= AT91C_OTGHS_EPT_TYPE_CTL_EPT) {
pInterface->OTGHS_DEVIDR = 1<<SHIFT_INTERUPT<<bEndpoint;
}
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS);
}
}
}
// SETUP packet received
if(ISSET(dStatus, AT91C_OTGHS_RXSTP)) {
TRACE_DEBUG_WP("Stp ");
// If a transfer was pending, complete it
// Handle 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->dState == endpointStateWrite)
|| (pEndpoint->dState == endpointStateRead)) {
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS);
}
// Copy the setup packet in S_usb
OTGHS_GetSetup(pUsb);
// Acknowledge setup packet
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXSTP;
// Forward the request to the upper layer
USB_NewRequestCallback(pUsb);
}
// STALL sent
if (ISSET(dStatus, AT91C_OTGHS_STALL)) {
TRACE_WARNING("Sta 0x%X [%d] ", dStatus, bEndpoint);
// Acknowledge STALL interrupt and disable it
pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_STALL;
//pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALL;
// If the endpoint is not halted, clear the stall condition
if (pEndpoint->dState != endpointStateHalted) {
TRACE_WARNING("_ " );
// Acknowledge the stall RQ flag
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALLRQ;
}
}
}
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// \brief Configure an endpoint with the provided endpoint descriptor
// \param pUsb Pointer to a S_usb instance
// \param pEpDesc Pointer to the endpoint descriptor
// \return true if the endpoint is now configured, false otherwise
// \see S_usb_endpoint_descriptor
// \see S_usb
//------------------------------------------------------------------------------
static bool OTGHS_ConfigureEndpoint(const S_usb *pUsb,
const S_usb_endpoint_descriptor *pEpDesc)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
S_usb_endpoint *pEndpoint;
unsigned char bEndpoint;
unsigned char bType;
unsigned char endpointDir;
unsigned short sizeEpt = 0;
// Maximum packet size configuration value
if( pEpDesc->wMaxPacketSize == 8 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_8;
} else if ( pEpDesc->wMaxPacketSize == 16 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_16;
} else if ( pEpDesc->wMaxPacketSize == 32 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_32;
} else if ( pEpDesc->wMaxPacketSize == 64 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_64;
} else if ( pEpDesc->wMaxPacketSize == 128 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_128;
} else if ( pEpDesc->wMaxPacketSize == 256 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_256;
} else if ( pEpDesc->wMaxPacketSize == 512 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_512;
} else if ( pEpDesc->wMaxPacketSize == 1024 ) {
sizeEpt = AT91C_OTGHS_EPT_SIZE_1024;
} //else {
// sizeEpt = 0; // control endpoint
//}
// if pEpDesc == 0 then initialize the control endpoint
if (pEpDesc == (S_usb_endpoint_descriptor const *) 0) {
bEndpoint = 0;
bType = 0; // Control endpoint
}
else {
// The endpoint number
bEndpoint = (unsigned char) (pEpDesc->bEndpointAddress & 0x7);
// Transfer type: Control, Isochronous, Bulk, Interrupt
bType = (unsigned char) (pEpDesc->bmAttributes & 0x3);
// Direction, ignored for control endpoints
endpointDir = (unsigned char) (pEpDesc->bEndpointAddress & (1<<7));
}
// Get pointer on endpoint
pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
if (pEndpoint == 0) {
return false;
}
// Configure wMaxPacketSize
if (pEpDesc != 0) {
pEndpoint->wMaxPacketSize = pEpDesc->wMaxPacketSize;
}
else {
pEndpoint->wMaxPacketSize = USB_ENDPOINT0_MAXPACKETSIZE;
}
// Abort the current transfer is the endpoint was configured and in
// Write or Read state
if ((pEndpoint->dState == endpointStateRead)
|| (pEndpoint->dState == endpointStateWrite)) {
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_RESET);
}
// Enter in IDLE state
pEndpoint->dState = endpointStateIdle;
// Reset Endpoint Fifos
pInterface->OTGHS_DEVEPT |= (1<<bEndpoint<<16);
pInterface->OTGHS_DEVEPT &= ~(1<<bEndpoint<<16);
// Enable endpoint
pInterface->OTGHS_DEVEPT |= (1<<bEndpoint);
// Configure endpoint
switch (bType) {
//-------------------------
case ENDPOINT_TYPE_CONTROL:
//-------------------------
TRACE_INFO("Control[%d]\n\r",bEndpoint);
//! Configure endpoint
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
AT91C_OTGHS_EPT_SIZE_64 | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_CTL_EPT | AT91C_OTGHS_BK_NUMBER_1;
// Enable RXSTP interrupt
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RXSTP;
// Enable endpoint IT
pInterface->OTGHS_DEVIER = 1<<SHIFT_INTERUPT<<bEndpoint;
break;
//-----------------------------
case ENDPOINT_TYPE_ISOCHRONOUS:
//-----------------------------
if (endpointDir) {
TRACE_INFO("Iso In[%d]\n\r",bEndpoint);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
else {
TRACE_INFO("Iso Out[%d]\n\r",bEndpoint);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
break;
//----------------------
case ENDPOINT_TYPE_BULK:
//----------------------
if (endpointDir) {
TRACE_INFO("Bulk In(%d)[%d] ",bEndpoint, pEpDesc->wMaxPacketSize);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
else {
TRACE_INFO("Bulk Out(%d)[%d]\n\r",bEndpoint, pEpDesc->wMaxPacketSize);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
break;
//---------------------------
case ENDPOINT_TYPE_INTERRUPT:
//---------------------------
if (endpointDir) {
TRACE_INFO("Interrupt In[%d]\n\r",bEndpoint);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
else {
TRACE_INFO("Interrupt Out[%d]\n\r",bEndpoint);
//! Configure endpoint
#ifndef DMA
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2;
#else
pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW |
sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2;
#endif
}
break;
//------
default:
//------
TRACE_ERROR(" unknown endpoint type\n\r");
return false;
}
// Check if the configuration is ok
if (ISCLEARED(pInterface->OTGHS_DEVEPTCSR[bEndpoint], AT91C_OTGHS_CFGOK)) {
TRACE_FATAL("OTGHS_ConfigureEndpoint: Cannot configure endpoint\n\r");
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Interrupt service routine
//------------------------------------------------------------------------------
#ifdef DMA
//----------------------------------------------------------------------------
//! \fn OTGHS_DmaHandler
//! \brief This function (ISR) handles DMA interrupts
//----------------------------------------------------------------------------
static void OTGHS_DmaHandler(const S_usb *pUsb, unsigned char endpoint)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, endpoint);
unsigned int csr;
csr = pInterface->OTGHS_DEVDMA[endpoint].OTGHS_DEVDMASTATUS;
pInterface->OTGHS_DEVIDR = (1<<SHIFT_DMA<<endpoint);
if((csr & AT91C_OTGHS_END_BF_ST) || (csr & AT91C_OTGHS_END_TR_ST)) {
// READ
TRACE_DEBUG_M("END_BF_ST\n\r");
pEndpoint->dBytesTransferred = pEndpoint->dBytesBuffered;
pEndpoint->dBytesBuffered = 0;
TRACE_DEBUG_M("dBytesBuffered: 0x%x\n\r",pEndpoint->dBytesBuffered);
TRACE_DEBUG_M("dBytesRemaining: 0x%x\n\r",pEndpoint->dBytesRemaining);
TRACE_DEBUG_M("dBytesTransferred: 0x%x\n\r",pEndpoint->dBytesTransferred);
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS);
pEndpoint->dState = endpointStateIdle;
}
else {
TRACE_FATAL("Probleme IT DMA\n\r");
}
}
#endif
//------------------------------------------------------------------------------
// \brief OTGHS interrupt handler
//
// Manages device resume, suspend, end of bus reset. Forwards endpoint
// interrupts to the appropriate handler.
// \param pUsb Pointer to a S_usb instance
//------------------------------------------------------------------------------
static void OTGHS_Handler(const S_usb *pUsb)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
unsigned int dStatus;
unsigned char numIT;
if ( (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED))
&& (ISSET(USB_GetState(pUsb), USB_STATE_POWERED))){
LED_TOGGLE(LED_USB);
}
TRACE_DEBUG_H("Hlr ");
// Get General interrupts status
dStatus = pInterface->OTGHS_SR & pInterface->OTGHS_CTRL & 0xFF;
while (dStatus != 0) {
if(ISSET(dStatus, AT91C_OTGHS_VBUSTI))
{
TRACE_DEBUG_M("__VBus\n\r");
USB_Attach(pUsb);
// Acknowledge the interrupt
pInterface->OTGHS_SCR = AT91C_OTGHS_VBUSTI;
}
// Don't treat others interrupt for this time
pInterface->OTGHS_SCR = AT91C_OTGHS_IDT | AT91C_OTGHS_SRP
| AT91C_OTGHS_VBERR | AT91C_OTGHS_BCERR
| AT91C_OTGHS_ROLEEX | AT91C_OTGHS_HNPERR
| AT91C_OTGHS_STO;
dStatus = pInterface->OTGHS_SR & pInterface->OTGHS_CTRL & 0xFF;
}
// Get OTG Device interrupts status
dStatus = pInterface->OTGHS_DEVISR & pInterface->OTGHS_DEVIMR;
TRACE_DEBUG_H("OTGHS_DEVISR:0x%X\n\r", pInterface->OTGHS_DEVISR);
while (dStatus != 0) {
// Start Of Frame (SOF)
if (ISSET(dStatus, AT91C_OTGHS_SOF)) {
TRACE_DEBUG_WP("SOF ");
// Invoke the SOF callback
USB_StartOfFrameCallback(pUsb);
// Acknowledge interrupt
SET(pInterface->OTGHS_DEVICR, AT91C_OTGHS_SOF);
CLEAR(dStatus, AT91C_OTGHS_SOF);
}
// Suspend
else if (dStatus & AT91C_OTGHS_SUSP) {
TRACE_DEBUG_M("S ");
if (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) {
// The device enters the Suspended state
// MCK + UDPCK must be off
// Pull-Up must be connected
// Transceiver must be disabled
// Enable wakeup
SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_EORST | AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM);
// Acknowledge interrupt
pInterface->OTGHS_DEVICR = AT91C_OTGHS_SUSP;
SET(*(pUsb->pState), USB_STATE_SUSPENDED);
OTGHS_DisableTransceiver(pUsb);
OTGHS_DisableMCK(pUsb);
OTGHS_DisableOTGHSCK(pUsb);
// Invoke the Suspend callback
USB_SuspendCallback(pUsb);
}
}
// Resume
else if (ISSET(dStatus, AT91C_OTGHS_WAKEUP)
|| ISSET(dStatus, AT91C_OTGHS_EORSM)) {
// Invoke the Resume callback
USB_ResumeCallback(pUsb);
TRACE_DEBUG_M("R ");
// The device enters Configured state
// MCK + UDPCK must be on
// Pull-Up must be connected
// Transceiver must be enabled
if (ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) {
// Powered state
OTGHS_EnableMCK(pUsb);
OTGHS_EnableOTGHSCK(pUsb);
// Default state
if (ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) {
OTGHS_EnableTransceiver(pUsb);
}
CLEAR(*(pUsb->pState), USB_STATE_SUSPENDED);
}
pInterface->OTGHS_DEVICR =
(AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM | AT91C_OTGHS_SUSP);
pInterface->OTGHS_DEVIER = (AT91C_OTGHS_EORST | AT91C_OTGHS_SUSP);
pInterface->OTGHS_DEVICR = (AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM);
pInterface->OTGHS_DEVIDR = AT91C_OTGHS_WAKEUP;
}
// End of bus reset
else if (dStatus & AT91C_OTGHS_EORST) {
TRACE_DEBUG_M("EoB ");
// The device enters the Default state
// MCK + UDPCK are already enabled
// Pull-Up is already connected
// Transceiver must be enabled
// Endpoint 0 must be enabled
SET(*(pUsb->pState), USB_STATE_DEFAULT);
OTGHS_EnableTransceiver(pUsb);
// The device leaves the Address & Configured states
CLEAR(*(pUsb->pState), USB_STATE_ADDRESS | USB_STATE_CONFIGURED);
OTGHS_ResetEndpoints(pUsb);
OTGHS_DisableEndpoints(pUsb);
OTGHS_ConfigureEndpoint(pUsb, 0);
// Flush and enable the Suspend interrupt
SET(pInterface->OTGHS_DEVICR, AT91C_OTGHS_WAKEUP | AT91C_OTGHS_SUSP);
// Enable the Start Of Frame (SOF) interrupt if needed
if (pUsb->pCallbacks->startOfFrame != 0) {
SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_SOF);
}
// Invoke the Reset callback
USB_ResetCallback(pUsb);
// Acknowledge end of bus reset interrupt
pInterface->OTGHS_DEVICR = AT91C_OTGHS_EORST;
}
// Handle upstream resume interrupt
else if (dStatus & AT91C_OTGHS_UPRSM) {
TRACE_DEBUG_WP(" External resume interrupt\n\r");
// - Acknowledge the IT
pInterface->OTGHS_DEVICR = AT91C_OTGHS_UPRSM;
}
// Endpoint interrupts
else {
#ifndef DMA
// Handle endpoint interrupts
for (numIT = 0; numIT < NUM_IT_MAX; numIT++) {
if( dStatus & (1<<SHIFT_INTERUPT<<numIT) ) {
OTGHS_EndpointHandler(pUsb, numIT);
}
}
#else
// Handle endpoint control interrupt
if( dStatus & (1<<SHIFT_INTERUPT<<0) ) {
OTGHS_EndpointHandler(pUsb, 0);
}
// Handle DMA interrupts
for(numIT = 1; numIT <= NUM_IT_MAX_DMA; numIT++) {
if( dStatus & (1<<SHIFT_DMA<<numIT) ) {
OTGHS_DmaHandler(pUsb, numIT);
}
}
#endif
}
// Retrieve new interrupt status
dStatus = (pInterface->OTGHS_DEVISR) & (pInterface->OTGHS_DEVIMR);
// Mask unneeded interrupts
if (!ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) {
dStatus &= AT91C_OTGHS_EORST | AT91C_OTGHS_SOF;
}
TRACE_DEBUG_H("\n\r");
if (dStatus != 0) {
TRACE_DEBUG_WP(" - ");
}
}
if ( (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED))
&& (ISSET(USB_GetState(pUsb), USB_STATE_POWERED))){
LED_TOGGLE(LED_USB);
}
}
//------------------------------------------------------------------------------
// \brief Sends data through an USB endpoint
//
// Sets up the transfer descriptor, write one or two data payloads
// (depending on the number of FIFO banks for the endpoint) and then
// starts the actual transfer. The operation is complete when all
// the data has been sent.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \param pData Pointer to a buffer containing the data to send
// \param dLength Length of the data buffer
// \param fCallback Optional function to invoke when the transfer finishes
// \param pArgument Optional argument for the callback function
// \return Operation result code
// \see Operation result codes
// \see Callback_f
// \see S_usb
//------------------------------------------------------------------------------
static char OTGHS_Write(const S_usb *pUsb,
unsigned char bEndpoint,
const void *pData,
unsigned int dLength,
Callback_f fCallback,
void *pArgument)
{
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
// Check that the endpoint is in Idle state
if (pEndpoint->dState != endpointStateIdle) {
return USB_STATUS_LOCKED;
}
TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength);
// Setup the transfer descriptor
pEndpoint->pData = (char *) pData;
pEndpoint->dBytesRemaining = dLength;
pEndpoint->dBytesBuffered = 0;
pEndpoint->dBytesTransferred = 0;
pEndpoint->fCallback = fCallback;
pEndpoint->pArgument = pArgument;
pEndpoint->isDataSent = false;
// Send one packet
pEndpoint->dState = endpointStateWrite;
#ifdef DMA
// Test if endpoint type control
if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])) {
#endif
// Enable endpoint IT
pInterface->OTGHS_DEVIER = (1<<SHIFT_INTERUPT<<bEndpoint);
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_TXINI;
#ifdef DMA
}
else {
// others endoint (not control)
pEndpoint->dBytesBuffered = pEndpoint->dBytesRemaining;
pEndpoint->dBytesRemaining = 0;
pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int) pEndpoint->pData;
// Enable IT DMA
pInterface->OTGHS_DEVIER = (1<<SHIFT_DMA<<bEndpoint);
pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL =
(((pEndpoint->dBytesBuffered<<16)&AT91C_OTGHS_BUFF_LENGTH)
| AT91C_OTGHS_END_B_EN
| AT91C_OTGHS_END_BUFFIT
| AT91C_OTGHS_CHANN_ENB);
}
#endif
return USB_STATUS_SUCCESS;
}
//------------------------------------------------------------------------------
// \brief 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
// packet size) is received.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \param pData Pointer to a buffer to store the received data
// \param dLength Length of the receive buffer
// \param fCallback Optional callback function
// \param pArgument Optional callback argument
// \return Operation result code
// \see Callback_f
// \see S_usb
//------------------------------------------------------------------------------
static char OTGHS_Read(const S_usb *pUsb,
unsigned char bEndpoint,
void *pData,
unsigned int dLength,
Callback_f fCallback,
void *pArgument)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
//! Return if the endpoint is not in IDLE state
if (pEndpoint->dState != endpointStateIdle) {
return USB_STATUS_LOCKED;
}
TRACE_DEBUG_M("Read%d(%d) ", bEndpoint, dLength);
// Endpoint enters Read state
pEndpoint->dState = endpointStateRead;
//! Set the transfer descriptor
pEndpoint->pData = (char *) pData;
pEndpoint->dBytesRemaining = dLength;
pEndpoint->dBytesBuffered = 0;
pEndpoint->dBytesTransferred = 0;
pEndpoint->fCallback = fCallback;
pEndpoint->pArgument = pArgument;
#ifdef DMA
// Test if endpoint type control
if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])) {
#endif
// Control endpoint
// Enable endpoint IT
pInterface->OTGHS_DEVIER = (1<<SHIFT_INTERUPT<<bEndpoint);
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RXOUT;
#ifdef DMA
}
else {
// others endoint (not control)
pEndpoint->dBytesBuffered = pEndpoint->dBytesRemaining;
pEndpoint->dBytesRemaining = 0;
// Enable IT DMA
pInterface->OTGHS_DEVIER = (1<<SHIFT_DMA<<bEndpoint);
pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int) pEndpoint->pData;
pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = \
( (pEndpoint->dBytesBuffered<<16)
| AT91C_OTGHS_END_TR_EN
| AT91C_OTGHS_END_TR_IT
| AT91C_OTGHS_END_B_EN
| AT91C_OTGHS_END_BUFFIT
| AT91C_OTGHS_CHANN_ENB);
}
#endif
return USB_STATUS_SUCCESS;
}
//------------------------------------------------------------------------------
// \brief Clears, sets or returns the Halt state on specified endpoint
//
// When in Halt state, an endpoint acknowledges every received packet
// with a STALL handshake. This continues until the endpoint is
// manually put out of the Halt state by calling this function.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \param bRequest Request to perform
// -> USB_SET_FEATURE, USB_CLEAR_FEATURE, USB_GET_STATUS
// \return true if the endpoint is currently Halted, false otherwise
// \see S_usb
//------------------------------------------------------------------------------
static bool OTGHS_Halt(const S_usb *pUsb,
unsigned char bEndpoint,
unsigned char bRequest)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
// Clear the Halt feature of the endpoint if it is enabled
if (bRequest == USB_CLEAR_FEATURE) {
TRACE_DEBUG_WP("Unhalt%d ", bEndpoint);
// Return endpoint to Idle state
pEndpoint->dState = endpointStateIdle;
// Clear FORCESTALL flag
// Disable stall on endpoint
pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALLRQ;
pEndpoint->dState = endpointStateIdle;
// Reset data-toggle
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RSTDT;
}
// Set the Halt feature on the endpoint if it is not already enabled
// and the endpoint is not disabled
else if ((bRequest == USB_SET_FEATURE)
&& (pEndpoint->dState != endpointStateHalted)
&& (pEndpoint->dState != endpointStateDisabled)) {
TRACE_DEBUG_WP("Halt%d ", bEndpoint);
// Abort the current transfer if necessary
OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_ABORTED);
// Put endpoint into Halt state
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALLRQ;
pEndpoint->dState = endpointStateHalted;
// Enable the endpoint interrupt
pInterface->OTGHS_DEVIER = (1<<SHIFT_INTERUPT<<bEndpoint);
}
// Return the endpoint halt status
if (pEndpoint->dState == endpointStateHalted) {
return true;
}
else {
return false;
}
}
//------------------------------------------------------------------------------
// \brief Causes the endpoint to acknowledge the next received packet with
// a STALL handshake.
//
// Further packets are then handled normally.
// \param pUsb Pointer to a S_usb instance
// \param bEndpoint Index of endpoint
// \return Operation result code
// \see S_usb
//------------------------------------------------------------------------------
static char OTGHS_Stall(const S_usb *pUsb,
unsigned char bEndpoint)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint);
// Check that endpoint is in Idle state
if (pEndpoint->dState != endpointStateIdle) {
TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint);
return USB_STATUS_LOCKED;
}
TRACE_DEBUG_WP("Stall%d ", bEndpoint);
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALL;
pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALLRQ;
return USB_STATUS_SUCCESS;
}
//------------------------------------------------------------------------------
// \brief Activates a remote wakeup procedure
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_RemoteWakeUp(const S_usb *pUsb)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
OTGHS_EnableMCK(pUsb);
OTGHS_EnableOTGHSCK(pUsb);
OTGHS_EnableTransceiver(pUsb);
TRACE_DEBUG_WP("Remote WakeUp ");
//! Enable wakeup interrupt
//pInterface->OTGHS_DEVIER = AT91C_OTGHS_UPRSM;
// Activates a remote wakeup
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_RMWKUP;
}
//------------------------------------------------------------------------------
// \brief Handles attachment or detachment from the USB when the VBus power
// line status changes.
// \param pUsb Pointer to a S_usb instance
// \return true if VBus is present, false otherwise
// \see S_usb
//------------------------------------------------------------------------------
static bool OTGHS_Attach(const S_usb *pUsb)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
TRACE_DEBUG_WP("Attach(");
// Check if VBus is present
if (!ISSET(USB_GetState(pUsb), USB_STATE_POWERED)
&& BRD_IsVBusConnected(pInterface)) {
// Powered state:
// MCK + UDPCK must be on
// Pull-Up must be connected
// Transceiver must be disabled
// Invoke the Resume callback
USB_ResumeCallback(pUsb);
OTGHS_EnableMCK(pUsb);
OTGHS_EnableOTGHSCK(pUsb);
// Enable the transceiver
OTGHS_EnableTransceiver(pUsb);
// Reconnect the pull-up if needed
if (ISSET(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT)) {
USB_Connect(pUsb);
CLEAR(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT);
}
// Clear the Suspend and Resume interrupts
pInterface->OTGHS_DEVICR = \
AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM | AT91C_OTGHS_SUSP;
// Enable interrupt
pInterface->OTGHS_DEVIER = AT91C_OTGHS_EORST | AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM;
// The device is in Powered state
SET(*(pUsb->pState), USB_STATE_POWERED);
}
else if (ISSET(USB_GetState(pUsb), USB_STATE_POWERED)
&& !BRD_IsVBusConnected(pInterface)) {
// Attached state:
// MCK + UDPCK off
// Pull-Up must be disconnected
// Transceiver must be disabled
// Warning: MCK must be enabled to be able to write in UDP registers
// It may have been disabled by the Suspend interrupt, so re-enable it
OTGHS_EnableMCK(pUsb);
// Disable interrupts
pInterface->OTGHS_DEVIDR &= ~(AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM
| AT91C_OTGHS_SUSP | AT91C_OTGHS_SOF);
OTGHS_DisableEndpoints(pUsb);
// Disconnect the pull-up if needed
if (ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) {
USB_Disconnect(pUsb);
SET(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT);
}
OTGHS_DisableTransceiver(pUsb);
OTGHS_DisableMCK(pUsb);
OTGHS_DisableOTGHSCK(pUsb);
// The device leaves the all states except Attached
CLEAR(*(pUsb->pState), USB_STATE_POWERED | USB_STATE_DEFAULT
| USB_STATE_ADDRESS | USB_STATE_CONFIGURED | USB_STATE_SUSPENDED);
// Invoke the Suspend callback
USB_SuspendCallback(pUsb);
}
TRACE_DEBUG_WP("%d) ", ISSET(USB_GetState(pUsb), USB_STATE_POWERED));
return ISSET(USB_GetState(pUsb), USB_STATE_POWERED);
}
//------------------------------------------------------------------------------
// \brief Sets the device address
//
// This function directly accesses the S_usb_request instance located
// in the S_usb structure to extract its new address.
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_SetAddress(S_usb const *pUsb)
{
unsigned short wAddress = USB_GetSetup(pUsb)->wValue;
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
TRACE_DEBUG_WP("SetAddr(%d) ", wAddress);
// Set address
pInterface->OTGHS_DEVCTRL = wAddress & AT91C_OTGHS_UADD;
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_ADDEN;
}
//------------------------------------------------------------------------------
// \brief Changes the device state from Address to Configured, or from
// Configured to Address.
//
// This method directly access the last received SETUP packet to
// decide on what to do.
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_SetConfiguration(S_usb const *pUsb)
{
unsigned short wValue = USB_GetSetup(pUsb)->wValue;
TRACE_DEBUG_WP("SetCfg() ");
// Check the request
if (wValue != 0) {
// Enter Configured state
SET(*(pUsb->pState), USB_STATE_CONFIGURED);
}
else {
// Go back to Address state
CLEAR(*(pUsb->pState), USB_STATE_CONFIGURED);
// Abort all transfers
OTGHS_DisableEndpoints(pUsb);
}
}
//------------------------------------------------------------------------------
// \brief Enables the pull-up on the D+ line to connect the device to the USB.
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_Connect(const S_usb *pUsb)
{
#if defined(INTERNAL_PULLUP)
CLEAR(OTGHS_GetDriverInterface(pUsb)->OTGHS_DEVCTRL, AT91C_OTGHS_DETACH);
#elif defined(INTERNAL_PULLUP_MATRIX)
TRACE_DEBUG_WP("PUON 1\n\r");
AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON;
#else
BRD_ConnectPullUp(UDP_GetDriverInterface(pUsb));
#endif
}
//------------------------------------------------------------------------------
// \brief Disables the pull-up on the D+ line to disconnect the device from
// the bus.
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_Disconnect(const S_usb *pUsb)
{
#if defined(INTERNAL_PULLUP)
SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_DEVCTRL, AT91C_OTGHS_DETACH);
#elif defined(INTERNAL_PULLUP_MATRIX)
TRACE_DEBUG_WP("PUON 0\n\r");
AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON;
#else
BRD_DisconnectPullUp(UDP_GetDriverInterface(pUsb));
#endif
// Device leaves the Default state
CLEAR(*(pUsb->pState), USB_STATE_DEFAULT);
}
//------------------------------------------------------------------------------
// \brief Certification test for High Speed device.
// \param pUsb Pointer to a S_usb instance
// \param bIndex char for the test choice
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_Test(const S_usb *pUsb, unsigned char bIndex)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
pInterface->OTGHS_DEVIDR &= ~AT91C_OTGHS_SUSP;
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_SPDCONF_HS; // remove suspend ?
switch( bIndex ) {
case TEST_PACKET:
TRACE_DEBUG_M("TEST_PACKET ");
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTPCKT;
break;
case TEST_J:
TRACE_DEBUG_M("TEST_J ");
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTJ;
break;
case TEST_K:
TRACE_DEBUG_M("TEST_K ");
pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTK;
break;
case TEST_SEO_NAK:
TRACE_DEBUG_M("TEST_SEO_NAK ");
pInterface->OTGHS_DEVIDR = 0xFFFFFFFF;
break;
case TEST_SEND_ZLP:
pInterface->OTGHS_DEVEPTCCR[0] = AT91C_OTGHS_TXINI;
TRACE_DEBUG_M("SEND_ZLP ");
break;
TRACE_DEBUG_M("\n\r");
}
}
//------------------------------------------------------------------------------
// \brief Certification test for High Speed device.
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static bool OTGHS_IsHighSpeed(const S_usb *pUsb)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
bool status = false;
if(AT91C_OTGHS_SPEED_SR_HS == (pInterface->OTGHS_SR & (0x03<<12))) {
// High Speed
status = true;
}
return status;
}
//------------------------------------------------------------------------------
// \brief Initializes the specified USB driver
//
// This function initializes the current FIFO bank of endpoints,
// configures the pull-up and VBus lines, disconnects the pull-up and
// then trigger the Init callback.
// \param pUsb Pointer to a S_usb instance
// \see S_usb
//------------------------------------------------------------------------------
static void OTGHS_Init(const S_usb *pUsb)
{
AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb);
unsigned char i;
TRACE_DEBUG_WP("Init()\n\r");
// Enable USB macro
SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_USBECTRL);
pInterface->OTGHS_DEVCTRL &=~ AT91C_OTGHS_DETACH; // detach
//// Force FS (for debug or test)
// pDriver->OTGHS_DEVCTRL |= AT91C_OTGHS_SPDCONF_FS;
pInterface->OTGHS_DEVCTRL &= ~AT91C_OTGHS_SPDCONF_FS; // Normal mode
pInterface->OTGHS_DEVCTRL &= ~( AT91C_OTGHS_LS | AT91C_OTGHS_TSTJ
| AT91C_OTGHS_TSTK | AT91C_OTGHS_TSTPCKT
| AT91C_OTGHS_OPMODE2 ); // Normal mode
// With OR without DMA !!!
// Initialization of DMA
for( i=1; i<=((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4); i++ ) {
// RESET endpoint canal DMA:
// DMA stop channel command
AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command
// Disable endpoint
AT91C_BASE_OTGHS->OTGHS_DEVEPTCDR[i] = 0XFFFFFFFF;
// Reset endpoint config
AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[i] = 0;
// Reset DMA channel (Buff count and Control field)
AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0x02; // NON STOP command
// Reset DMA channel 0 (STOP)
AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command
// Clear DMA channel status (read the register for clear it)
AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS = AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS;
}
// Enable clock OTG pad
pInterface->OTGHS_CTRL &= ~AT91C_OTGHS_FRZCLKCTRL;
// Clear General IT
pInterface->OTGHS_SCR = 0x01FF;
// Clear OTG Device IT
pInterface->OTGHS_DEVICR = 0xFF;
// Clear OTG Host IT
pInterface->OTGHS_HSTICR = 0x7F;
// Reset all Endpoints Fifos
pInterface->OTGHS_DEVEPT |= (0x7F<<16);
pInterface->OTGHS_DEVEPT &= ~(0x7F<<16);
// Disable all endpoints
pInterface->OTGHS_DEVEPT &= ~0x7F;
// Bypass UTMI problems // jcb to be removed with new version of UTMI
// pInterface->OTGHS_TSTA2 = (1<<6)|(1<<7)|(1<<8);
// pInterface->OTGHS_TSTA2 = (1<<8);
pInterface->OTGHS_TSTA2 = 0;
// External pull-up on D+
// Configure
BRD_ConfigurePullUp(pInterface);
// Detach
OTGHS_Disconnect(pUsb);
// Device is in the Attached state
*(pUsb->pState) = USB_STATE_ATTACHED;
// Disable the UDP transceiver and interrupts
OTGHS_EnableMCK(pUsb);
SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_EORSM);
OTGHS_DisableMCK(pUsb);
OTGHS_Disconnect(pUsb);
// Test ID
if( 0 != (pInterface->OTGHS_SR & AT91C_OTGHS_ID) ) {
TRACE_INFO("ID=1: PERIPHERAL\n\r");
}
else {
TRACE_INFO("ID=0: HOST\n\r");
}
// Test VBUS
if( 0 != (pInterface->OTGHS_SR & AT91C_OTGHS_VBUSSR) ) {
TRACE_INFO("VBUS = 1\n\r");
}
else {
TRACE_INFO("VBUS = 0\n\r");
}
// Test SPEED
if(AT91C_OTGHS_SPEED_SR_HS == (pInterface->OTGHS_SR & (0x03<<12))) {
TRACE_INFO("HIGH SPEED\n\r");
}
else if(AT91C_OTGHS_SPEED_SR_LS == (pInterface->OTGHS_SR & (0x03<<12))) {
TRACE_INFO("LOW SPEED\n\r");
}
else {
TRACE_INFO("FULL SPEED\n\r");
}
// Configure interrupts
USB_InitCallback(pUsb);
pInterface->OTGHS_CTRL |= AT91C_OTGHS_VBUSTI;
}
//------------------------------------------------------------------------------
// Global variables
//------------------------------------------------------------------------------
// \brief Low-level driver methods to use with the OTGHS USB controller
// \see S_driver_methods
const S_driver_methods sOTGHSMethods = {
OTGHS_Init,
OTGHS_Write,
OTGHS_Read,
OTGHS_Stall,
OTGHS_Halt,
OTGHS_RemoteWakeUp,
OTGHS_ConfigureEndpoint,
OTGHS_Attach,
OTGHS_SetAddress,
OTGHS_SetConfiguration,
OTGHS_Handler,
OTGHS_Connect,
OTGHS_Disconnect,
OTGHS_Test,
OTGHS_IsHighSpeed
};
// \brief Default driver when an UDP controller is present on a chip
const S_usb_driver sDefaultDriver = {
AT91C_BASE_OTGHS,
AT91C_BASE_OTGHS_EPTFIFO,
0,
AT91C_ID_OTGHS,
AT91C_PMC_OTG,
&sOTGHSMethods
};
#endif //#ifdef CHIP_OTGHS