/* | |
FreeRTOS+TCP V2.0.11 | |
Copyright (C) 2017 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 | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "semphr.h" | |
/* FreeRTOS+TCP includes. */ | |
#include "FreeRTOS_IP.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "FreeRTOS_IP_Private.h" | |
#include "NetworkBufferManagement.h" | |
#include "NetworkInterface.h" | |
/* Xilinx library files. */ | |
#include <xemacps.h> | |
#include "Zynq/x_topology.h" | |
#include "Zynq/x_emacpsif.h" | |
#include "Zynq/x_emacpsif_hw.h" | |
/* Provided memory configured as uncached. */ | |
#include "uncached_memory.h" | |
#ifndef BMSR_LINK_STATUS | |
#define BMSR_LINK_STATUS 0x0004UL | |
#endif | |
#ifndef PHY_LS_HIGH_CHECK_TIME_MS | |
/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not | |
receiving packets. */ | |
#define PHY_LS_HIGH_CHECK_TIME_MS 15000 | |
#endif | |
#ifndef PHY_LS_LOW_CHECK_TIME_MS | |
/* Check if the LinkSStatus in the PHY is still low every second. */ | |
#define PHY_LS_LOW_CHECK_TIME_MS 1000 | |
#endif | |
/* 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 | |
/* Naming and numbering of PHY registers. */ | |
#define PHY_REG_01_BMSR 0x01 /* Basic mode status register */ | |
#ifndef iptraceEMAC_TASK_STARTING | |
#define iptraceEMAC_TASK_STARTING() do { } while( 0 ) | |
#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 | |
/*-----------------------------------------------------------*/ | |
/* | |
* Look for the link to be up every few milliseconds until either xMaxTime time | |
* has passed or a link is found. | |
*/ | |
static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ); | |
/* | |
* A deferred interrupt handler for all MAC/DMA interrupt sources. | |
*/ | |
static void prvEMACHandlerTask( void *pvParameters ); | |
/*-----------------------------------------------------------*/ | |
/* EMAC data/descriptions. */ | |
static xemacpsif_s xEMACpsif; | |
struct xtopology_t xXTopology = | |
{ | |
.emac_baseaddr = XPAR_PS7_ETHERNET_0_BASEADDR, | |
.emac_type = xemac_type_emacps, | |
.intc_baseaddr = 0x0, | |
.intc_emac_intr = 0x0, | |
.scugic_baseaddr = XPAR_PS7_SCUGIC_0_BASEADDR, | |
.scugic_emac_intr = 0x36, | |
}; | |
XEmacPs_Config mac_config = | |
{ | |
.DeviceId = XPAR_PS7_ETHERNET_0_DEVICE_ID, /**< Unique ID of device */ | |
.BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR /**< Physical base address of IPIF registers */ | |
}; | |
extern int phy_detected; | |
/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ | |
static uint32_t ulPHYLinkStatus = 0; | |
#if( ipconfigUSE_LLMNR == 1 ) | |
static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; | |
#endif | |
/* ucMACAddress as it appears in main.c */ | |
extern const uint8_t ucMACAddress[ 6 ]; | |
/* Holds the handle of the task used as a deferred interrupt processor. The | |
handle is used so direct notifications can be sent to the task for all EMAC/DMA | |
related interrupts. */ | |
TaskHandle_t xEMACTaskHandle = NULL; | |
/*-----------------------------------------------------------*/ | |
BaseType_t xNetworkInterfaceInitialise( void ) | |
{ | |
uint32_t ulLinkSpeed, ulDMAReg; | |
BaseType_t xStatus, xLinkStatus; | |
XEmacPs *pxEMAC_PS; | |
const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL ); | |
/* Guard against the init function being called more than once. */ | |
if( xEMACTaskHandle == NULL ) | |
{ | |
pxEMAC_PS = &( xEMACpsif.emacps ); | |
memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) ); | |
xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress); | |
if( xStatus != XST_SUCCESS ) | |
{ | |
FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) ); | |
} | |
/* Initialize the mac and set the MAC address. */ | |
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ucMACAddress, 1 ); | |
#if( ipconfigUSE_LLMNR == 1 ) | |
{ | |
/* Also add LLMNR multicast MAC address. */ | |
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * )xLLMNR_MACAddress, 2 ); | |
} | |
#endif /* ipconfigUSE_LLMNR == 1 */ | |
XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 ); | |
ulLinkSpeed = Phy_Setup( pxEMAC_PS ); | |
XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed); | |
/* Setting the operating speed of the MAC needs a delay. */ | |
vTaskDelay( pdMS_TO_TICKS( 25UL ) ); | |
ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET); | |
/* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive | |
packets from the receiver packet buffer memory when no AHB resource is available. */ | |
XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET, | |
ulDMAReg | XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK); | |
setup_isr( &xEMACpsif ); | |
init_dma( &xEMACpsif ); | |
start_emacps( &xEMACpsif ); | |
prvGMACWaitLS( xWaitLinkDelay ); | |
/* The deferred interrupt handler task is created at the highest | |
possible priority to ensure the interrupt handler can return directly | |
to it. The task's handle is stored in xEMACTaskHandle so interrupts can | |
notify the task when there is something to process. */ | |
xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); | |
} | |
else | |
{ | |
/* Initialisation was already performed, just wait for the link. */ | |
prvGMACWaitLS( xWaitRelinkDelay ); | |
} | |
/* Only return pdTRUE when the Link Status of the PHY is high, otherwise the | |
DHCP process and all other communication will fail. */ | |
xLinkStatus = xGetPhyLinkStatus(); | |
return ( xLinkStatus != pdFALSE ); | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer, BaseType_t bReleaseAfterSend ) | |
{ | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
iptraceNETWORK_INTERFACE_TRANSMIT(); | |
emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend ); | |
} | |
else if( bReleaseAfterSend != pdFALSE ) | |
{ | |
/* No link. */ | |
vReleaseNetworkBufferAndDescriptor( pxBuffer ); | |
} | |
return pdTRUE; | |
} | |
/*-----------------------------------------------------------*/ | |
static inline unsigned long ulReadMDIO( unsigned ulRegister ) | |
{ | |
uint16_t usValue; | |
XEmacPs_PhyRead( &( xEMACpsif.emacps ), phy_detected, ulRegister, &usValue ); | |
return usValue; | |
} | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ) | |
{ | |
TickType_t xStartTime, xEndTime; | |
const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL ); | |
BaseType_t xReturn; | |
xStartTime = xTaskGetTickCount(); | |
for( ;; ) | |
{ | |
xEndTime = xTaskGetTickCount(); | |
if( xEndTime - xStartTime > xMaxTime ) | |
{ | |
xReturn = pdFALSE; | |
break; | |
} | |
ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
xReturn = pdTRUE; | |
break; | |
} | |
vTaskDelay( xShortDelay ); | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) | |
{ | |
static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) ); | |
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( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) | |
{ | |
xReturn = pdFALSE; | |
} | |
else | |
{ | |
xReturn = pdTRUE; | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvEMACHandlerTask( void *pvParameters ) | |
{ | |
TimeOut_t xPhyTime; | |
TickType_t xPhyRemTime; | |
UBaseType_t uxLastMinBufferCount = 0; | |
UBaseType_t uxCurrentCount; | |
BaseType_t xResult = 0; | |
uint32_t xStatus; | |
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); | |
/* Remove compiler warnings about unused parameters. */ | |
( void ) pvParameters; | |
/* A possibility to set some additional task properties like calling | |
portTASK_USES_FLOATING_POINT() */ | |
iptraceEMAC_TASK_STARTING(); | |
vTaskSetTimeOutState( &xPhyTime ); | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); | |
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 ) ); | |
} | |
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) | |
{ | |
static UBaseType_t uxLastMinQueueSpace = 0; | |
uxCurrentCount = uxGetMinimumIPQueueSpace(); | |
if( uxLastMinQueueSpace != uxCurrentCount ) | |
{ | |
/* The logging produced below may be helpful | |
while tuning +TCP: see how many buffers are in use. */ | |
uxLastMinQueueSpace = uxCurrentCount; | |
FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); | |
} | |
} | |
#endif /* ipconfigCHECK_IP_QUEUE_SPACE */ | |
if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 ) | |
{ | |
/* No events to process now, wait for the next. */ | |
ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT; | |
xResult = emacps_check_rx( &xEMACpsif ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT; | |
emacps_check_tx( &xEMACpsif ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT; | |
emacps_check_errors( &xEMACpsif ); | |
} | |
if( xResult > 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( PHY_LS_HIGH_CHECK_TIME_MS ); | |
xResult = 0; | |
} | |
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) | |
{ | |
xStatus = ulReadMDIO( PHY_REG_01_BMSR ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) | |
{ | |
ulPHYLinkStatus = xStatus; | |
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); | |
} | |
vTaskSetTimeOutState( &xPhyTime ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); | |
} | |
else | |
{ | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ |