/* * 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 "FreeRTOS.h" #include "list.h" #include "queue.h" #include "semphr.h" #include "task.h" /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "FreeRTOS_IP_Private.h" #include "NetworkBufferManagement.h" #include "NetworkInterface.h" #include "m480_eth.h" /* 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 /* Default the size of the stack used by the EMAC deferred handler task to twice * the size of the stack used by the idle task - but allow this to be overridden in * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ #ifndef configEMAC_TASK_STACK_SIZE #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE ) #endif static SemaphoreHandle_t xTXMutex = NULL; /* The handle of the task that processes Rx packets. The handle is required so * the task can be notified when new packets arrive. */ static TaskHandle_t xRxHanderTask = NULL; static TimerHandle_t xPhyHandlerTask = NULL; /* * A task that processes received frames. */ static void prvEMACHandlerTask( void * pvParameters ); static void prvPhyTmrCallback( TimerHandle_t xTimer ); /* The size of each buffer when BufferAllocation_1 is used: * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */ #define niBUFFER_1_PACKET_SIZE 1536 #ifdef __ICCARM__ #pragma data_alignment=4 static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] #else static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 4 ) ) ); #endif BaseType_t xNetworkInterfaceInitialise( void ) { uint8_t hwaddr[ 6 ]; BaseType_t xReturn = pdPASS; /* Init ETH */ numaker_mac_address( hwaddr ); FreeRTOS_UpdateMACAddress( hwaddr ); FreeRTOS_printf( ( "mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", hwaddr[ 0 ], hwaddr[ 1 ], hwaddr[ 2 ], hwaddr[ 3 ], hwaddr[ 4 ], hwaddr[ 5 ] ) ); /* Enable clock & set EMAC configuration */ /* Enable MAC and DMA transmission and reception */ if( numaker_eth_init( hwaddr ) < 0 ) { xReturn = pdFAIL; } else { xReturn = pdPASS; /* Guard against the task being created more than once and the * descriptors being initialized more than once. */ /* Timer task to monitor PHY Link status */ if( xPhyHandlerTask == NULL ) { xPhyHandlerTask = xTimerCreate( "TimerPhy", pdMS_TO_TICKS( 1000 ), pdTRUE, 0, prvPhyTmrCallback ); configASSERT( xPhyHandlerTask ); xReturn = xTimerStart( xPhyHandlerTask, 0 ); configASSERT( xReturn ); } /* Rx task */ if( xRxHanderTask == NULL ) { xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask ); configASSERT( xReturn ); } if( xTXMutex == NULL ) { xTXMutex = xSemaphoreCreateMutex(); configASSERT( xTXMutex ); } } NVIC_SetPriority( EMAC_RX_IRQn, configMAC_INTERRUPT_PRIORITY ); NVIC_SetPriority( EMAC_TX_IRQn, configMAC_INTERRUPT_PRIORITY ); numaker_eth_enable_interrupts(); FreeRTOS_printf( ( "ETH-RX priority:%d\n", NVIC_GetPriority( EMAC_RX_IRQn ) ) ); return xReturn; } BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t xReleaseAfterSend ) { uint8_t * buffer = NULL; if( pxDescriptor->xDataLength >= PACKET_BUFFER_SIZE ) { FreeRTOS_printf( ( "TX buffer length %d over %d\n", pxDescriptor->xDataLength, PACKET_BUFFER_SIZE ) ); return pdFALSE; } buffer = numaker_eth_get_tx_buf(); if( buffer == NULL ) { NU_DEBUGF( ( "Eth TX slots are busy\n" ) ); return pdFALSE; } /* Get exclusive access */ xSemaphoreTake( xTXMutex, portMAX_DELAY ); NU_DEBUGF( ( "%s ... buffer=0x%x\r\n", __FUNCTION__, buffer ) ); /*SendData: pt = pxDescriptor->pucBuffer, length = pxDescriptor->xDataLength */ memcpy( buffer, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); numaker_eth_trigger_tx( pxDescriptor->xDataLength, NULL ); /* Call the standard trace macro to log the send event. */ iptraceNETWORK_INTERFACE_TRANSMIT(); if( xReleaseAfterSend != pdFALSE ) { /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet * buffer. The Ethernet buffer is therefore no longer needed, and must be * freed for re-use. */ vReleaseNetworkBufferAndDescriptor( pxDescriptor ); } xSemaphoreGive( xTXMutex ); return pdTRUE; } void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) { uint8_t * ucRAMBuffer = ucNetworkPackets; uint32_t ul; for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) { pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); ucRAMBuffer += niBUFFER_1_PACKET_SIZE; } } BaseType_t xGetPhyLinkStatus( void ) { BaseType_t xReturn; if( numaker_eth_link_ok() ) { xReturn = pdPASS; } else { xReturn = pdFAIL; } return xReturn; } static void prvPhyTmrCallback( TimerHandle_t xTimer ) { IPStackEvent_t xRxEvent; static BaseType_t lastLink = pdFAIL; BaseType_t currLink = xGetPhyLinkStatus(); if( currLink != lastLink ) { FreeRTOS_printf( ( "PHY Link %s\n", ( currLink ) ? "Up" : "Down" ) ); if( !currLink ) { xRxEvent.eEventType = eNetworkDownEvent; xSendEventStructToIPTask( &xRxEvent, 0 ); } lastLink = currLink; } } static void prvEMACHandlerTask( void * pvParameters ) { TimeOut_t xPhyTime; TickType_t xPhyRemTime; UBaseType_t uxLastMinBufferCount = 0; UBaseType_t uxCurrentCount; BaseType_t xResult = 0; uint32_t ulStatus; uint16_t dataLength = 0; uint8_t * buffer = NULL; NetworkBufferDescriptor_t * pxBufferDescriptor = NULL; IPStackEvent_t xRxEvent; const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul ); /* Remove compiler warnings about unused parameters. */ ( void ) pvParameters; /* A possibility to set some additional task properties. */ for( ; ; ) { uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); if( uxLastMinBufferCount != uxCurrentCount ) { /* The logging produced below may be helpful * while tuning +TCP: see how many buffers are in use. */ uxLastMinBufferCount = uxCurrentCount; FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); } /* No events to process now, wait for the next. */ ulTaskNotifyTake( pdFALSE, portMAX_DELAY ); while( 1 ) { /* get received frame */ if( numaker_eth_get_rx_buf( &dataLength, &buffer ) != 0 ) { /* The event was lost because a network buffer was not available. * Call the standard trace macro to log the occurrence. */ iptraceETHERNET_RX_EVENT_LOST(); break; } /* Allocate a network buffer descriptor that points to a buffer * large enough to hold the received frame. As this is the simple * rather than efficient example the received data will just be copied * into this buffer. */ pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( PACKET_BUFFER_SIZE, 0 ); if( pxBufferDescriptor != NULL ) { memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer, dataLength ); pxBufferDescriptor->xDataLength = dataLength; } else { numaker_eth_rx_next(); iptraceETHERNET_RX_EVENT_LOST(); break; } /* The event about to be sent to the TCP/IP is an Rx event. */ xRxEvent.eEventType = eNetworkRxEvent; /* pvData is used to point to the network buffer descriptor that * now references the received data. */ xRxEvent.pvData = ( void * ) pxBufferDescriptor; /* Send the data to the TCP/IP stack. */ if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) { /* The buffer could not be sent to the IP task so the buffer * must be released. */ vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); /* Make a call to the standard trace macro to log the * occurrence. */ iptraceETHERNET_RX_EVENT_LOST(); } else { /* The message was successfully sent to the TCP/IP stack. * Call the standard trace macro to log the occurrence. */ iptraceNETWORK_INTERFACE_RECEIVE(); } numaker_eth_rx_next(); } numaker_eth_trigger_rx(); } } void xNetworkCallback( char event ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; switch( event ) { case 'R': /*For RX event */ /* Wakeup the prvEMACHandlerTask. */ if( xRxHanderTask != NULL ) { vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } break; case 'T': /*For TX event */ /* ack of tx done, no-op in this stage */ break; default: break; } }