blob: 30ac1dabab71cefd0f335f2359e4ca92bfbd8944 [file] [log] [blame]
/*
* FreeRTOS+UDP V1.0.3 (C) 2014 Real Time Engineers ltd.
* All rights reserved
*
* This file is part of the FreeRTOS+UDP distribution. The FreeRTOS+UDP license
* terms are different to the FreeRTOS license terms.
*
* FreeRTOS+UDP uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+UDP is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+UDP is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals that for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a commercial license before
* incorporating FreeRTOS+UDP into proprietary software for distribution in any
* form. Commercial licenses can be purchased from http://shop.freertos.org/udp
* and do not require any source files to be changed.
*
* FreeRTOS+UDP is distributed in the hope that it will be useful. You cannot
* use FreeRTOS+UDP unless you agree that you use the software 'as is'.
* FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
* implied, expressed, or statutory.
*
* 1 tab == 4 spaces!
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/udp
*
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* FreeRTOS+UDP includes. */
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkBufferManagement.h"
/* Library includes. */
#include "board.h"
/* Descriptors that reference received buffers are expected to have both the
first and last frame bits set because buffers are dimensioned to hold complete
Ethernet frames. */
#define emacEXPECTED_RX_STATUS_MASK ( RDES_LS | RDES_FS )
/*-----------------------------------------------------------*/
/*
* Set the Rx and Tx descriptors into their expected initial state.
*/
static void prvResetRxDescriptors( void );
static void prvResetTxDescriptors( void );
/*
* Returns the length of the data pointed to by the next Rx descriptor.
*/
static uint32_t prvReceivedDataLength( void );
/*-----------------------------------------------------------*/
/* Rx and Tx descriptors and data array. */
static volatile IP_ENET_001_ENHRXDESC_T xRXDescriptors[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];
static volatile IP_ENET_001_ENHTXDESC_T xTxDescriptors[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS ];
/* Indexes into the Rx and Tx descriptor arrays. */
static unsigned int xRxDescriptorIndex = 0;
static unsigned int xTxDescriptorIndex = 0;
/*-----------------------------------------------------------*/
portBASE_TYPE xEMACInit( uint8_t ucMACAddress[ 6 ] )
{
portBASE_TYPE xReturn;
uint32_t ulPHYStatus;
/* Configure the hardware. */
Chip_ENET_Init( LPC_ETHERNET );
if( lpc_phy_init( pdTRUE, vTaskDelay ) == SUCCESS )
{
/* The MAC address is passed in as the function parameter. */
Chip_ENET_SetADDR( LPC_ETHERNET, ucMACAddress );
/* Wait for autonegotiation to complete. */
do
{
vTaskDelay( 100 );
ulPHYStatus = lpcPHYStsPoll();
} while( ( ulPHYStatus & PHY_LINK_CONNECTED ) == 0x00 );
/* Configure the hardware as per the negotiated link. */
if( ( ulPHYStatus & PHY_LINK_FULLDUPLX ) == PHY_LINK_FULLDUPLX )
{
IP_ENET_SetDuplex( LPC_ETHERNET, pdTRUE );
}
else
{
IP_ENET_SetDuplex( LPC_ETHERNET, pdFALSE );
}
if( ( ulPHYStatus & PHY_LINK_SPEED100 ) == PHY_LINK_SPEED100 )
{
IP_ENET_SetSpeed( LPC_ETHERNET, pdTRUE );
}
else
{
IP_ENET_SetSpeed( LPC_ETHERNET, pdFALSE );
}
/* Set descriptors to their initial state. */
prvResetRxDescriptors();
prvResetTxDescriptors();
/* Enable RX and TX. */
Chip_ENET_TXEnable( LPC_ETHERNET );
Chip_ENET_RXEnable( LPC_ETHERNET );
/* Enable the interrupt and set its priority as configured. THIS
DRIVER REQUIRES configMAC_INTERRUPT_PRIORITY TO BE DEFINED, PREFERABLY
IN FreeRTOSConfig.h. */
NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );
NVIC_EnableIRQ( ETHERNET_IRQn );
/* Enable interrupts. */
LPC_ETHERNET->DMA_INT_EN = DMA_IE_NIE | DMA_IE_RIE;
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
portBASE_TYPE xEMACIsTxDescriptorAvailable( void )
{
portBASE_TYPE xReturn;
if( ( xTxDescriptors[ xTxDescriptorIndex ].CTRLSTAT & RDES_OWN ) == 0 )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
void vEMACAssignBufferToDescriptor( uint8_t * pucBuffer )
{
/* The old packet is now finished with and can be freed. */
vEthernetBufferRelease( ( void * ) xTxDescriptors[ xTxDescriptorIndex ].B1ADD );
/* Assign the new packet to the descriptor. */
xTxDescriptors[ xTxDescriptorIndex ].B1ADD = ( uint32_t ) pucBuffer;
}
/*-----------------------------------------------------------*/
void vEMACStartNextTransmission( uint32_t ulLength )
{
xTxDescriptors[ xTxDescriptorIndex ].BSIZE = ulLength;
xTxDescriptors[ xTxDescriptorIndex ].CTRLSTAT |= RDES_OWN;
/* Wake Up the DMA if it's in Suspended Mode. */
LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
xTxDescriptorIndex++;
if( xTxDescriptorIndex == configNUM_TX_ETHERNET_DMA_DESCRIPTORS )
{
xTxDescriptorIndex = 0;
}
}
/*-----------------------------------------------------------*/
static uint32_t prvReceivedDataLength( void )
{
unsigned short RxLen = 0;
RxLen = ( xRXDescriptors[ xRxDescriptorIndex ].STATUS >> 16 ) & 0x03FFF;
return RxLen;
}
/*-----------------------------------------------------------*/
void vEMACReturnRxDescriptor( void )
{
xRXDescriptors[ xRxDescriptorIndex ].STATUS = RDES_OWN;
xRxDescriptorIndex++;
if( xRxDescriptorIndex == configNUM_RX_ETHERNET_DMA_DESCRIPTORS )
{
xRxDescriptorIndex = 0;
}
}
/*-----------------------------------------------------------*/
portBASE_TYPE xEMACRxDataAvailable( void )
{
portBASE_TYPE xReturn;
if( ( xRXDescriptors[ xRxDescriptorIndex ].STATUS & RDES_OWN ) == 0 )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
void vEMACSwapEmptyBufferForRxedData( xNetworkBufferDescriptor_t *pxNetworkBuffer )
{
uint8_t *pucTemp;
/* Swap the buffer in the network buffer with the buffer used by the DMA.
This allows the data to be passed out without having to perform any copies. */
pucTemp = ( uint8_t * ) xRXDescriptors[ xRxDescriptorIndex ].B1ADD;
xRXDescriptors[ xRxDescriptorIndex ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
pxNetworkBuffer->pucEthernetBuffer = pucTemp;
/* Only supports frames coming in single buffers. If this frame is split
across multiple buffers then reject it (and if the frame is needed increase
the ipconfigNETWORK_MTU setting). */
if( ( xRXDescriptors[ xRxDescriptorIndex ].STATUS & emacEXPECTED_RX_STATUS_MASK ) != emacEXPECTED_RX_STATUS_MASK )
{
pxNetworkBuffer->xDataLength = 0;
}
else
{
pxNetworkBuffer->xDataLength = ( size_t ) prvReceivedDataLength() - ( ipETHERNET_CRC_BYTES - 1U );;
}
}
/*-----------------------------------------------------------*/
static void prvResetRxDescriptors( void )
{
uint32_t x;
size_t xBufferSize = ipTOTAL_ETHERNET_FRAME_SIZE;
for( x = 0; x < configNUM_RX_ETHERNET_DMA_DESCRIPTORS; x++ )
{
/* Obtain the buffer first, as the size of the buffer might be changed
within the pucEthernetBufferGet() call. */
xRXDescriptors[ x ].B1ADD = ( uint32_t ) pucEthernetBufferGet( &xBufferSize );
xRXDescriptors[ x ].STATUS = RDES_OWN;
xRXDescriptors[ x ].CTRL = xBufferSize;
xRXDescriptors[ x ].B2ADD = ( uint32_t ) &xRXDescriptors[ x + 1 ];
configASSERT( ( ( ( uint32_t ) xRXDescriptors[x].B1ADD ) & 0x07 ) == 0 );
}
/* Last Descriptor */
xRXDescriptors[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS - 1 ].CTRL |= RDES_ENH_RER;
xRxDescriptorIndex = 0;
/* Set Starting address of RX Descriptor list */
LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xRXDescriptors;
}
/*-----------------------------------------------------------*/
static void prvResetTxDescriptors( void )
{
/* Initialize Transmit Descriptor and Status array. */
uint32_t x;
for( x = 0; x < configNUM_TX_ETHERNET_DMA_DESCRIPTORS; x++ )
{
xTxDescriptors[ x ].CTRLSTAT = TDES_ENH_FS | TDES_ENH_LS;
xTxDescriptors[ x ].BSIZE = 0;
xTxDescriptors[ x ].B2ADD = ( uint32_t ) &xTxDescriptors[ x + 1 ];
/* Packet is assigned when a Tx is initiated. */
xTxDescriptors[ x ].B1ADD = ( uint32_t )NULL;
}
/* Last Descriptor? */
xTxDescriptors[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS-1 ].CTRLSTAT |= TDES_ENH_TER;
/* Set Starting address of TX Descriptor list */
LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xTxDescriptors;
}
/*-----------------------------------------------------------*/
void ETH_IRQHandler( void )
{
uint32_t ulInterruptCause;
extern xSemaphoreHandle xEMACRxEventSemaphore;
configASSERT( xEMACRxEventSemaphore );
ulInterruptCause = LPC_ETHERNET->DMA_STAT ;
/* Clear the interrupt. */
LPC_ETHERNET->DMA_STAT |= ( DMA_ST_NIS | DMA_ST_RI );
/* Clear fatal error conditions. NOTE: The driver does not clear all
errors, only those actually experienced. For future reference, range
errors are not actually errors so can be ignored. */
if( ( ulInterruptCause & DMA_ST_FBI ) != 0U )
{
LPC_ETHERNET->DMA_STAT |= DMA_ST_FBI;
}
/* Unblock the deferred interrupt handler task if the event was an Rx. */
if( ( ulInterruptCause & DMA_IE_RIE ) != 0UL )
{
xSemaphoreGiveFromISR( xEMACRxEventSemaphore, NULL );
}
/* ulInterruptCause is used for convenience here. A context switch is
wanted, but coding portEND_SWITCHING_ISR( 1 ) would likely result in a
compiler warning. */
portEND_SWITCHING_ISR( ulInterruptCause );
}