/*********************************************************************************************************************** * DISCLAIMER * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all * applicable laws, including copyright laws. * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of * this software. By using this software, you agree to the additional terms and conditions found by accessing the * following link: * http://www.renesas.com/disclaimer * * Copyright (C) 2020 Renesas Electronics Corporation. All rights reserved. ***********************************************************************************************************************/ /*********************************************************************************************************************** * File Name : NetworkInterface.c * Device(s) : RX * Description : Interfaces FreeRTOS TCP/IP stack to RX Ethernet driver. ***********************************************************************************************************************/ /*********************************************************************************************************************** * History : DD.MM.YYYY Version Description * : 07.03.2018 0.1 Development ***********************************************************************************************************************/ /*********************************************************************************************************************** * Includes , "Project Includes" ***********************************************************************************************************************/ #include #include #include #include /* FreeRTOS includes. */ #include "FreeRTOS.h" #include "task.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_IP_Private.h" /*#include "FreeRTOS_DNS.h" */ #include "NetworkBufferManagement.h" #include "NetworkInterface.h" #include "r_ether_rx_if.h" #include "r_pinset.h" /*********************************************************************************************************************** * Macro definitions **********************************************************************************************************************/ #define ETHER_BUFSIZE_MIN 60 #if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) || defined( BSP_MCU_RX72M ) || defined( BSP_MCU_RX72N ) #if ETHER_CFG_MODE_SEL == 0 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_MII() #elif ETHER_CFG_MODE_SEL == 1 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC0_RMII() #endif #elif defined( BSP_MCU_RX63N ) #if ETHER_CFG_MODE_SEL == 0 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_MII() #elif ETHER_CFG_MODE_SEL == 1 #define R_ETHER_PinSet_CHANNEL_0() R_ETHER_PinSet_ETHERC_RMII() #endif #endif /* if defined( BSP_MCU_RX65N ) || defined( BSP_MCU_RX64M ) || defined( BSP_MCU_RX71M ) */ #if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS ) #error please use the new defines with 'ipconfig' prefix #endif #ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS /* Check if the LinkStatus in the PHY is still high after 2 seconds of not * receiving packets. */ #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS 2000U #endif #ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS /* Check if the LinkStatus in the PHY is still low every second. */ #define ipconfigPHY_LS_LOW_CHECK_TIME_MS 1000U #endif /*********************************************************************************************************************** * Private global variables and functions **********************************************************************************************************************/ typedef enum { eMACInit, /* Must initialise MAC. */ eMACPass, /* Initialisation was successful. */ eMACFailed, /* Initialisation failed. */ } eMAC_INIT_STATUS_TYPE; static TaskHandle_t ether_receive_check_task_handle = NULL; static TaskHandle_t xTaskToNotify = NULL; static BaseType_t xPHYLinkStatus; static BaseType_t xReportedStatus; static eMAC_INIT_STATUS_TYPE xMacInitStatus = eMACInit; /* Pointer to the interface object of this NIC */ static NetworkInterface_t * pxMyInterface = NULL; static int16_t SendData( uint8_t * pucBuffer, size_t length ); static int InitializeNetwork( void ); static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ); static void clear_all_ether_rx_discriptors( uint32_t event ); int32_t callback_ether_regist( void ); void EINT_Trig_isr( void * ); void get_random_number( uint8_t * data, uint32_t len ); void prvLinkStatusChange( BaseType_t xStatus ); /*-----------------------------------------------------------*/ NetworkInterface_t * pxRX_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ); /* Function to initialise the network interface */ BaseType_t xRX_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface ); BaseType_t xRX_NetworkInterfaceOutput( NetworkInterface_t * pxInterface, NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend ); static inline BaseType_t xRX_PHYGetLinkStatus( NetworkInterface_t * pxInterface ); NetworkInterface_t * pxRX_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { static char pcName[ 17 ]; /* This function pxRX_FillInterfaceDescriptor() adds a network-interface. * Make sure that the object pointed to by 'pxInterface' * is declared static or global, and that it will remain to exist. */ snprintf( pcName, sizeof( pcName ), "eth%u", ( unsigned ) xEMACIndex ); memset( pxInterface, '\0', sizeof( *pxInterface ) ); pxInterface->pcName = pcName; /* Just for logging, debugging. */ pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */ pxInterface->pfInitialise = xRX_NetworkInterfaceInitialise; pxInterface->pfOutput = xRX_NetworkInterfaceOutput; pxInterface->pfGetPhyLinkStatus = xRX_PHYGetLinkStatus; FreeRTOS_AddNetworkInterface( pxInterface ); return pxInterface; } /*********************************************************************************************************************** * Function Name: xRX_NetworkInterfaceInitialise () * Description : Initialization of Ethernet driver. * Arguments : Pointer to the interface desc * Return Value : pdPASS, pdFAIL **********************************************************************************************************************/ BaseType_t xRX_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface ) { BaseType_t xReturn; if( xMacInitStatus == eMACInit ) { pxMyInterface = pxInterface; /* * Perform the hardware specific network initialization here using the Ethernet driver library to initialize the * Ethernet hardware, initialize DMA descriptors, and perform a PHY auto-negotiation to obtain a network link. * * InitialiseNetwork() uses Ethernet peripheral driver library function, and returns 0 if the initialization fails. */ if( InitializeNetwork() == pdFALSE ) { xMacInitStatus = eMACFailed; } else { /* Indicate that the MAC initialisation succeeded. */ xMacInitStatus = eMACPass; } FreeRTOS_printf( ( "InitializeNetwork returns %s\n", ( xMacInitStatus == eMACPass ) ? "OK" : " Fail" ) ); } if( xMacInitStatus == eMACPass ) { xReturn = xPHYLinkStatus; } else { xReturn = pdFAIL; } FreeRTOS_printf( ( "xNetworkInterfaceInitialise returns %d\n", xReturn ) ); return xReturn; } /* End of function xNetworkInterfaceInitialise() */ /*********************************************************************************************************************** * Function Name: xRX_NetworkInterfaceOutput () * Description : Simple network output interface. * Arguments : pxInterface, pxDescriptor, xReleaseAfterSend * Return Value : pdTRUE, pdFALSE **********************************************************************************************************************/ BaseType_t xRX_NetworkInterfaceOutput( NetworkInterface_t * pxInterface, NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t xReleaseAfterSend ) { BaseType_t xReturn = pdFALSE; /* As there is only a single instance of the EMAC, there is only one pxInterface object. */ ( void ) pxInterface; /* Simple network interfaces (as opposed to more efficient zero copy network * interfaces) just use Ethernet peripheral driver library functions to copy * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer. * This example assumes SendData() is a peripheral driver library function that * takes a pointer to the start of the data to be sent and the length of the * data to be sent as two separate parameters. The start of the data is located * by pxDescriptor->pucEthernetBuffer. The length of the data is located * by pxDescriptor->xDataLength. */ if( xPHYLinkStatus != 0 ) { if( SendData( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ) >= 0 ) { xReturn = pdTRUE; /* Call the standard trace macro to log the send event. */ iptraceNETWORK_INTERFACE_TRANSMIT(); } } else { /* As the PHY Link Status is low, it makes no sense trying to deliver a packet. */ } 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 ); } return xReturn; } /* End of function xNetworkInterfaceOutput() */ /*********************************************************************************************************************** * Function Name: prvEMACDeferredInterruptHandlerTask () * Description : The deferred interrupt handler is a standard RTOS task. * Arguments : pvParameters * Return Value : none **********************************************************************************************************************/ static void prvEMACDeferredInterruptHandlerTask( void * pvParameters ) { NetworkBufferDescriptor_t * pxBufferDescriptor; int32_t xBytesReceived = 0; /* Avoid compiler warning about unreferenced parameter. */ ( void ) pvParameters; /* Used to indicate that xSendEventStructToIPTask() is being called because * of an Ethernet receive event. */ IPStackEvent_t xRxEvent; uint8_t * buffer_pointer; /* Some variables related to monitoring the PHY. */ TimeOut_t xPhyTime; TickType_t xPhyRemTime; const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); vTaskSetTimeOutState( &xPhyTime ); xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS ); FreeRTOS_printf( ( "Deferred Interrupt Handler Task started\n" ) ); xTaskToNotify = ether_receive_check_task_handle; for( ; ; ) { #if ( ipconfigHAS_PRINTF != 0 ) { /* Call a function that monitors resources: the amount of free network * buffers and the amount of free space on the heap. See FreeRTOS_IP.c * for more detailed comments. */ vPrintResourceStats(); } #endif /* ( ipconfigHAS_PRINTF != 0 ) */ /* Wait for the Ethernet MAC interrupt to indicate that another packet * has been received. */ if( xBytesReceived <= 0 ) { ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); } /* See how much data was received. */ xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer ); if( xBytesReceived < 0 ) { /* This is an error. Logged. */ FreeRTOS_debug_printf( ( "R_ETHER_Read_ZC2: rc = %d\n", xBytesReceived ) ); } else if( xBytesReceived > 0 ) { /* 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( ( size_t ) xBytesReceived, 0 ); if( pxBufferDescriptor != NULL ) { /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet * buffer large enough to hold the received data. Copy the * received data into pcNetworkBuffer->pucEthernetBuffer. Here it * is assumed ReceiveData() is a peripheral driver function that * copies the received data into a buffer passed in as the function's * parameter. Remember! While is is a simple robust technique - * it is not efficient. An example that uses a zero copy technique * is provided further down this page. */ memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer_pointer, ( size_t ) xBytesReceived ); /*ReceiveData( pxBufferDescriptor->pucEthernetBuffer ); */ /* Set the actual packet length, in case a larger buffer was returned. */ pxBufferDescriptor->xDataLength = ( size_t ) xBytesReceived; pxBufferDescriptor->pxInterface = pxMyInterface; pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxBufferDescriptor->pucEthernetBuffer ); R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 ); /* See if the data contained in the received Ethernet frame needs * to be processed. NOTE! It is preferable to do this in * the interrupt service routine itself, which would remove the need * to unblock this task for packets that don't need processing. */ if( ( eConsiderFrameForProcessing( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) && ( pxBufferDescriptor->pxEndPoint != NULL ) ) { /* 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(); clear_all_ether_rx_discriptors( 0 ); } else { /* The message was successfully sent to the TCP/IP stack. * Call the standard trace macro to log the occurrence. */ iptraceNETWORK_INTERFACE_RECEIVE(); R_BSP_NOP(); } } else { /* The Ethernet frame can be dropped, but the Ethernet buffer must be released. */ vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor ); } } else { /* The event was lost because a network buffer was not available. * Call the standard trace macro to log the occurrence. */ iptraceETHERNET_RX_EVENT_LOST(); clear_all_ether_rx_discriptors( 1 ); FreeRTOS_printf( ( "R_ETHER_Read_ZC2: Cleared descriptors\n" ) ); } } if( xBytesReceived > 0 ) { /* A packet was received. No need to check for the PHY status now, * but set a timer to check it later on. */ vTaskSetTimeOutState( &xPhyTime ); xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS ); /* Indicate that the Link Status is high, so that * xNetworkInterfaceOutput() can send packets. */ if( xPHYLinkStatus == 0 ) { xPHYLinkStatus = 1; FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume %d\n", xPHYLinkStatus ) ); } } else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) || ( FreeRTOS_IsNetworkUp() == pdFALSE ) ) { R_ETHER_LinkProcess( ETHER_CHANNEL_0 ); if( xPHYLinkStatus != xReportedStatus ) { xPHYLinkStatus = xReportedStatus; FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", xPHYLinkStatus ) ); } vTaskSetTimeOutState( &xPhyTime ); if( xPHYLinkStatus != 0 ) { xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS ); } else { xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS ); } } } } /* End of function prvEMACDeferredInterruptHandlerTask() */ /*********************************************************************************************************************** * Function Name: vNetworkInterfaceAllocateRAMToBuffers () * Description : . * Arguments : pxNetworkBuffers * Return Value : none **********************************************************************************************************************/ void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) { uint32_t ul; uint8_t * buffer_address; portPOINTER_SIZE_TYPE uxStartAddress; static uint8_t ETH_BUFFERS[ ( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ETHER_CFG_BUFSIZE ) + portBYTE_ALIGNMENT ]; /* Align the buffer start address to portBYTE_ALIGNMENT bytes */ uxStartAddress = ( portPOINTER_SIZE_TYPE ) & ETH_BUFFERS[ 0 ]; uxStartAddress += portBYTE_ALIGNMENT; uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ); buffer_address = ( uint8_t * ) uxStartAddress; for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) { pxNetworkBuffers[ ul ].pucEthernetBuffer = buffer_address + ipBUFFER_PADDING; *( ( unsigned * ) buffer_address ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); buffer_address += ETHER_CFG_BUFSIZE; } } /* End of function vNetworkInterfaceAllocateRAMToBuffers() */ /*********************************************************************************************************************** * Function Name: prvLinkStatusChange () * Description : Function will be called when the Link Status of the phy has changed ( see ether_callback.c ) * Arguments : xStatus : true when status has become high * Return Value : void **********************************************************************************************************************/ void prvLinkStatusChange( BaseType_t xStatus ) { if( xReportedStatus != xStatus ) { FreeRTOS_printf( ( "prvLinkStatusChange( %d )\n", xStatus ) ); xReportedStatus = xStatus; } } /*********************************************************************************************************************** * Function Name: InitializeNetwork () * Description : * Arguments : none * Return Value : pdTRUE, pdFALSE **********************************************************************************************************************/ static int InitializeNetwork( void ) { ether_return_t eth_ret; BaseType_t return_code = pdFALSE; ether_param_t param; /* Read the mac address after it has been initilized by the FreeRTOS IP Stack, rather than from defines * as the mac address is usually read from the EEPROM, and it might be different to the mac address in * the defines, especially in production environments */ configASSERT( pxMyInterface ); const uint8_t * myethaddr = &pxMyInterface->pxEndPoint->xMACAddress.ucBytes[ 0 ]; R_ETHER_PinSet_CHANNEL_0(); R_ETHER_Initial(); callback_ether_regist(); param.channel = ETHER_CHANNEL_0; eth_ret = R_ETHER_Control( CONTROL_POWER_ON, param ); /* PHY mode settings, module stop cancellation */ if( ETHER_SUCCESS != eth_ret ) { return pdFALSE; } eth_ret = R_ETHER_Open_ZC2( ETHER_CHANNEL_0, myethaddr, ETHER_FLAG_OFF ); if( ETHER_SUCCESS != eth_ret ) { return pdFALSE; } if( ether_receive_check_task_handle == NULL ) { return_code = xTaskCreate( prvEMACDeferredInterruptHandlerTask, "ETHER_RECEIVE_CHECK_TASK", 512u, 0, configMAX_PRIORITIES - 1, ðer_receive_check_task_handle ); } else { return_code = pdTRUE; } if( pdFALSE == return_code ) { return pdFALSE; } return pdTRUE; } /* End of function InitializeNetwork() */ /*********************************************************************************************************************** * Function Name: SendData () * Description : * Arguments : pucBuffer, length * Return Value : 0 success, negative fail **********************************************************************************************************************/ static int16_t SendData( uint8_t * pucBuffer, size_t length ) /*TODO complete stub function */ { ether_return_t ret; uint8_t * pwrite_buffer; uint16_t write_buf_size; /* (1) Retrieve the transmit buffer location controlled by the descriptor. */ ret = R_ETHER_Write_ZC2_GetBuf( ETHER_CHANNEL_0, ( void ** ) &pwrite_buffer, &write_buf_size ); if( ETHER_SUCCESS == ret ) { if( write_buf_size >= length ) { memcpy( pwrite_buffer, pucBuffer, length ); } if( length < ETHER_BUFSIZE_MIN ) /*under minimum*/ { memset( ( pwrite_buffer + length ), 0, ( ETHER_BUFSIZE_MIN - length ) ); /*padding*/ length = ETHER_BUFSIZE_MIN; /*resize*/ } ret = R_ETHER_Write_ZC2_SetBuf( ETHER_CHANNEL_0, ( uint16_t ) length ); ret = R_ETHER_CheckWrite( ETHER_CHANNEL_0 ); } if( ETHER_SUCCESS != ret ) { return -5; /* XXX return meaningful value */ } else { return 0; } } /* End of function SendData() */ /*********************************************************************************************************************** * Function Name: EINT_Trig_isr * Description : Standard frame received interrupt handler * Arguments : ectrl - EDMAC and ETHERC control structure * Return Value : None * Note : This callback function is executed when EINT0 interrupt occurred. ***********************************************************************************************************************/ void EINT_Trig_isr( void * ectrl ) { ether_cb_arg_t * pdecode; BaseType_t xHigherPriorityTaskWoken = pdFALSE; pdecode = ( ether_cb_arg_t * ) ectrl; if( pdecode->status_eesr & 0x00040000 ) /* EDMAC FR (Frame Receive Event) interrupt */ { if( xTaskToNotify != NULL ) { vTaskNotifyGiveFromISR( ether_receive_check_task_handle, &xHigherPriorityTaskWoken ); } /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch * should be performed to ensure the interrupt returns directly to the highest * priority task. The macro used for this purpose is dependent on the port in * use and may be called portEND_SWITCHING_ISR(). */ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); /*TODO complete interrupt handler for other events. */ } } /* End of function EINT_Trig_isr() */ static void clear_all_ether_rx_discriptors( uint32_t event ) { int32_t xBytesReceived; uint8_t * buffer_pointer; /* Avoid compiler warning about unreferenced parameter. */ ( void ) event; while( 1 ) { /* See how much data was received. */ xBytesReceived = R_ETHER_Read_ZC2( ETHER_CHANNEL_0, ( void ** ) &buffer_pointer ); if( 0 > xBytesReceived ) { /* This is an error. Ignored. */ } else if( 0 < xBytesReceived ) { R_ETHER_Read_ZC2_BufRelease( ETHER_CHANNEL_0 ); iptraceETHERNET_RX_EVENT_LOST(); } else { break; } } } static inline BaseType_t xRX_PHYGetLinkStatus( NetworkInterface_t * pxInterface ) { ( void ) pxInterface; return( xPHYLinkStatus != 0 ); } /*********************************************************************************************************************** * End of file "NetworkInterface.c" **********************************************************************************************************************/