/* * FreeRTOS+TCP * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://aws.amazon.com/freertos * http://www.FreeRTOS.org */ /* FreeRTOS includes. */ #include "LPC54018.h" #include "FreeRTOS.h" #include "list.h" #include /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_IP_Private.h" #include "NetworkBufferManagement.h" #include "fsl_enet.h" #include "fsl_phy.h" #include "fsl_enet_mdio.h" #include "fsl_phylan8720a.h" #include "fsl_debug_console.h" #define PHY_ADDRESS ( 0x00U ) /* MDIO operations. */ #define EXAMPLE_MDIO_OPS lpc_enet_ops /* PHY operations. */ #define EXAMPLE_PHY_OPS phylan8720a_ops #define ENET_RXBD_NUM ( 4 ) #define ENET_TXBD_NUM ( 4 ) #define ENET_RXBUFF_SIZE ( ENET_FRAME_MAX_FRAMELEN ) #define ENET_BuffSizeAlign( n ) ENET_ALIGN( n, ENET_BUFF_ALIGNMENT ) #define ENET_ALIGN( x, align ) ( ( unsigned int ) ( ( x ) + ( ( align ) - 1 ) ) & ( unsigned int ) ( ~( unsigned int ) ( ( align ) - 1 ) ) ) #if defined( __GNUC__ ) #ifndef __ALIGN_END #define __ALIGN_END __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) ) #endif #ifndef __ALIGN_BEGIN #define __ALIGN_BEGIN #endif #else #ifndef __ALIGN_END #define __ALIGN_END #endif #ifndef __ALIGN_BEGIN #if defined( __CC_ARM ) || defined( __ARMCC_VERSION ) #define __ALIGN_BEGIN __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) ) #elif defined( __ICCARM__ ) #define __ALIGN_BEGIN #endif #endif #endif /* if defined( __GNUC__ ) */ /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet * driver will filter incoming packets and only pass the stack those packets it * considers need processing. */ #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer #else #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) #endif #ifndef NETWORK_INTERFACE_RX_PRIORITY #define NETWORK_INTERFACE_RX_PRIORITY ( configMAX_PRIORITIES - 1 ) #endif /******************************************************************************* * Variables ******************************************************************************/ #if defined( __ICCARM__ ) #pragma data_alignment = ENET_BUFF_ALIGNMENT #endif __ALIGN_BEGIN enet_rx_bd_struct_t g_rxBuffDescrip[ ENET_RXBD_NUM ] __ALIGN_END; #if defined( __ICCARM__ ) #pragma data_alignment = ENET_BUFF_ALIGNMENT #endif __ALIGN_BEGIN enet_tx_bd_struct_t g_txBuffDescrip[ ENET_TXBD_NUM ] __ALIGN_END; enet_handle_t g_handle = { 0 }; /* The MAC address for ENET device. */ uint8_t g_macAddr[ 6 ] = { 0xde, 0xad, 0x00, 0xbe, 0xef, 0x01 }; bool g_linkStatus = false; /*! @brief Enet PHY and MDIO interface handler. */ static mdio_handle_t mdioHandle = { .ops = &EXAMPLE_MDIO_OPS }; static phy_handle_t phyHandle = { .phyAddr = PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS }; __ALIGN_BEGIN uint32_t receiveBuffer[ ENET_RXBD_NUM ][ ENET_RXBUFF_SIZE / sizeof( uint32_t ) + 1 ] __ALIGN_END; uint32_t rxbuffer[ ENET_RXBD_NUM ]; TaskHandle_t receiveTaskHandle; void ENET_IntCallback( ENET_Type * base, enet_handle_t * handle, enet_event_t event, uint8_t channel, void * param ) { BaseType_t needsToYield = pdFALSE; switch( event ) { case kENET_TxIntEvent: break; case kENET_RxIntEvent: vTaskNotifyGiveFromISR( receiveTaskHandle, &needsToYield ); portEND_SWITCHING_ISR( needsToYield ); break; default: break; } } static void prvProcessFrame( int length ) { NetworkBufferDescriptor_t * pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( length, 0 ); if( pxBufferDescriptor != NULL ) { ENET_ReadFrame( ENET, &g_handle, pxBufferDescriptor->pucEthernetBuffer, length, 0 ); pxBufferDescriptor->xDataLength = length; if( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) { IPStackEvent_t xRxEvent; xRxEvent.eEventType = eNetworkRxEvent; xRxEvent.pvData = ( void * ) pxBufferDescriptor; if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) { vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); iptraceETHERNET_RX_EVENT_LOST(); PRINTF( "RX Event Lost\n" ); } } else { PRINTF( "RX Event not to be considered\n" ); vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); /* Not sure if a trace is required. The stack did not want this message */ } } else { PRINTF( "RX No Buffer Available\n" ); ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 ); /* No buffer available to receive this message */ iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER(); } } static void rx_task( void * parameter ) { while( pdTRUE ) { if( ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( 500 ) ) == pdFALSE ) /* no RX packets for a bit so check for a link */ { PHY_GetLinkStatus( &phyHandle, &g_linkStatus ); } else { BaseType_t receiving = pdTRUE; while( receiving == pdTRUE ) { uint32_t length; const status_t status = ENET_GetRxFrameSize( ENET, &g_handle, &length, 0 ); switch( status ) { case kStatus_Success: /* there is a frame. process it */ if( length ) { prvProcessFrame( length ); } break; case kStatus_ENET_RxFrameEmpty: /* Received an empty frame. Ignore it */ receiving = pdFALSE; break; case kStatus_ENET_RxFrameError: /* Received an error frame. Read & drop it */ PRINTF( "RX Receive Error\n" ); ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 ); /* Not sure if a trace is required. The MAC had an error and needed to dump bytes */ break; default: PRINTF( "RX Receive default\n" ); break; } } } } } BaseType_t xGetPhyLinkStatus( void ) { return g_linkStatus ? pdTRUE : pdFALSE; } BaseType_t xNetworkInterfaceInitialise( void ) { BaseType_t returnValue = pdFAIL; static enum { initPhy, waitForLink, startReceiver, configurePhy } networkInitialisePhase = initPhy; switch( networkInitialisePhase ) { default: networkInitialisePhase = initPhy; /* fall through */ case initPhy: { phy_config_t phyConfig; phyConfig.phyAddr = PHY_ADDRESS; phyConfig.autoNeg = true; mdioHandle.resource.base = ENET; status_t status = PHY_Init( &phyHandle, &phyConfig ); if( status == kStatus_PHY_AutoNegotiateFail ) { PRINTF( "\nPHY Auto-negotiation failed. Please check the cable connection and link partner setting.\n" ); break; } } case startReceiver: networkInitialisePhase = startReceiver; if( xTaskCreate( rx_task, "rx_task", 512, NULL, NETWORK_INTERFACE_RX_PRIORITY, &receiveTaskHandle ) != pdPASS ) { PRINTF( "Network Receive Task creation failed!.\n" ); break; } /* fall through */ case waitForLink: networkInitialisePhase = waitForLink; { if( !xGetPhyLinkStatus() ) { PRINTF( "No Link\n" ); break; } } /* fall through */ case configurePhy: { networkInitialisePhase = configurePhy; enet_config_t config; phy_speed_t speed; phy_duplex_t duplex; PHY_GetLinkSpeedDuplex( &phyHandle, &speed, &duplex ); /* Get default configuration 100M RMII. */ ENET_GetDefaultConfig( &config ); /* Use the actual speed and duplex when phy success to finish the autonegotiation. */ config.miiSpeed = ( enet_mii_speed_t ) speed; config.miiDuplex = ( enet_mii_duplex_t ) duplex; /* Initialize ENET. */ uint32_t refClock = 50000000; /* 50MHZ for rmii reference clock. */ ENET_Init( ENET, &config, g_macAddr, refClock ); /* Enable the rx interrupt. */ ENET_EnableInterrupts( ENET, ( kENET_DmaRx ) ); /* Initialize Descriptor. */ int bufferIndex; for( bufferIndex = 0; bufferIndex < ENET_RXBD_NUM; bufferIndex++ ) { rxbuffer[ bufferIndex ] = ( uint32_t ) &receiveBuffer[ bufferIndex ]; } /* prepare the buffer configuration. */ enet_buffer_config_t buffConfig[ 1 ] = { { ENET_RXBD_NUM, ENET_TXBD_NUM, &g_txBuffDescrip[ 0 ], &g_txBuffDescrip[ 0 ], &g_rxBuffDescrip[ 0 ], &g_rxBuffDescrip[ ENET_RXBD_NUM ], &rxbuffer[ 0 ], ENET_BuffSizeAlign( ENET_RXBUFF_SIZE ), } }; ENET_DescriptorInit( ENET, &config, &buffConfig[ 0 ] ); /* Create the handler. */ ENET_CreateHandler( ENET, &g_handle, &config, &buffConfig[ 0 ], ENET_IntCallback, NULL ); NVIC_SetPriority( 65 - 16, 4 ); /* TODO this is a hack and I would expect a nice ENET API for priority. */ /* Active TX/RX. */ ENET_StartRxTx( ENET, 1, 1 ); } returnValue = pdPASS; break; } return returnValue; } BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t xReleaseAfterSend ) { BaseType_t response = pdFALSE; status_t status; if( xGetPhyLinkStatus() ) { status = ENET_SendFrame( ENET, &g_handle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); switch( status ) { default: /* anything not Success will be a failure */ case kStatus_ENET_TxFrameBusy: PRINTF( "TX Frame Busy\n" ); break; case kStatus_Success: iptraceNETWORK_INTERFACE_TRANSMIT(); response = pdTRUE; break; } } if( xReleaseAfterSend != pdFALSE ) { vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); } return response; } /* statically allocate the buffers */ /* allocating them as uint32_t's to force them into word alignment, a requirement of the DMA. */ __ALIGN_BEGIN static uint32_t buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ ( ipBUFFER_PADDING + ENET_RXBUFF_SIZE ) / sizeof( uint32_t ) + 1 ] __ALIGN_END; void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) { for( int x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ ) { pxNetworkBuffers[ x ].pucEthernetBuffer = ( uint8_t * ) &buffers[ x ][ 0 ] + ipBUFFER_PADDING; buffers[ x ][ 0 ] = ( uint32_t ) &pxNetworkBuffers[ x ]; } }