/* | |
FreeRTOS+TCP V2.0.11 | |
Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
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; | |
// FreeRTOS_printf(("<-- dataLength=%d\n",pxDescriptor->xDataLength)); | |
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 ); | |
// FreeRTOS_printf(("--> dataLength=%d\n",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; | |
} | |
} |