/* ---------------------------------------------------------------------------- * 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 #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<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<> 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<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<OTGHS_DEVEPT &= ~(1<OTGHS_DEVEPT |= (1<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<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<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<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<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<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<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<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<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