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