/* | |
* 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 ); | |
} | |