1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-24 12:02:37 +08:00
Files
FreeRTOS-Plus-TCP/source/portable/NetworkInterface/Zynq/NetworkInterface.c
Monika Singh dbd2b001b1 Update portable layer for backward compatibility (#755)
* Rename FreeRTOS_IPStart to FreeRTOS_IPInit_Multi

* Update backward compatability for portable layer
2023-03-07 22:02:55 +05:30

669 lines
25 KiB
C

/*
* FreeRTOS+TCP <DEVELOPMENT BRANCH>
* 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
*/
/* 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 "FreeRTOS_ARP.h"
#include "NetworkBufferManagement.h"
#include "NetworkInterface.h"
#include "FreeRTOS_DHCP.h"
#include "FreeRTOS_DNS.h"
#include "FreeRTOS_Routing.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 niEMAC_HANDLER_TASK_PRIORITY
/* Define the priority of the task prvEMACHandlerTask(). */
#define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
#endif
#define niBMSR_LINK_STATUS 0x0004uL
#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( ipFALSE_BOOL )
#endif
/* Default the size of the stack used by the EMAC deferred handler task to 8 times
* 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 ( 8 * configMINIMAL_STACK_SIZE )
#endif
#if ( ipconfigNIC_LINKSPEED100 != 1 )
/* When the PHY is forces to work with a speed of 100 Mbps
* many outgoing packets seem to get dropped.
*/
#warning ipconfigNIC_LINKSPEED100 is btoken. Are you sure?
#endif
static NetworkInterface_t * pxMyInterfaces[ XPAR_XEMACPS_NUM_INSTANCES ];
#if ( ipconfigZERO_COPY_RX_DRIVER == 0 || ipconfigZERO_COPY_TX_DRIVER == 0 )
#error Please define both 'ipconfigZERO_COPY_RX_DRIVER' and 'ipconfigZERO_COPY_TX_DRIVER' as 1
#endif
#if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 || ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
#warning Please define both 'ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM' and 'ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM' as 1
#endif
#ifndef nicUSE_UNCACHED_MEMORY
#define nicUSE_UNCACHED_MEMORY 1
#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( BaseType_t xEMACIndex,
TickType_t xMaxTime );
/*
* A deferred interrupt handler for all MAC/DMA interrupt sources.
*/
static void prvEMACHandlerTask( void * pvParameters );
/* FreeRTOS+TCP/multi :
* Each network device has 3 access functions:
* - initialise the device
* - output a network packet
* - return the PHY link-status (LS)
* They can be defined as static because their addresses will be
* stored in struct NetworkInterface_t. */
static BaseType_t xZynqNetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
static BaseType_t xZynqNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
NetworkBufferDescriptor_t * const pxBuffer,
BaseType_t bReleaseAfterSend );
static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface );
NetworkInterface_t * pxZynq_FillInterfaceDescriptor( BaseType_t xEMACIndex,
NetworkInterface_t * pxInterface );
/*-----------------------------------------------------------*/
/* EMAC data/descriptions. */
static xemacpsif_s xEMACpsifs[ XPAR_XEMACPS_NUM_INSTANCES ];
struct xtopology_t xXTopologies[ XPAR_XEMACPS_NUM_INSTANCES ] =
{
[ 0 ] =
{
.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,
},
#if ( XPAR_XEMACPS_NUM_INSTANCES > 1 )
[ 1 ] =
{
.emac_baseaddr = XPAR_PS7_ETHERNET_1_BASEADDR,
.emac_type = xemac_type_emacps,
.intc_baseaddr = 0x0,
.intc_emac_intr = 0x0,
.scugic_baseaddr = XPAR_PS7_SCUGIC_0_BASEADDR,
.scugic_emac_intr = 0x4D, /* See "7.2.3 Shared Peripheral Interrupts (SPI)" */
},
#endif
};
XEmacPs_Config mac_configs[ XPAR_XEMACPS_NUM_INSTANCES ] =
{
[ 0 ] =
{
.DeviceId = XPAR_PS7_ETHERNET_0_DEVICE_ID, /**< Unique ID of device, used for 'xEMACIndex' */
.BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR /**< Physical base address of IPIF registers */
},
#if ( XPAR_XEMACPS_NUM_INSTANCES > 1 )
[ 1 ] =
{
.DeviceId = XPAR_PS7_ETHERNET_1_DEVICE_ID, /**< Unique ID of device */
.BaseAddress = XPAR_PS7_ETHERNET_1_BASEADDR /**< Physical base address of IPIF registers */
},
#endif
};
extern int phy_detected[ XPAR_XEMACPS_NUM_INSTANCES ];
/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
static uint32_t ulPHYLinkStates[ XPAR_XEMACPS_NUM_INSTANCES ];
/* 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 xEMACTaskHandles[ XPAR_XEMACPS_NUM_INSTANCES ];
/*-----------------------------------------------------------*/
/**
* @brief Initialise the interface number 'xIndex'
* @param xIndex: the index of the interface, between 0
* zero and (XPAR_XEMACPS_NUM_INSTANCES-1)
* @note Although the function is declared public, it should
* not be called directly by an application.
*/
void vInitialiseOnIndex( BaseType_t xIndex )
{
if( ( xIndex >= 0 ) && ( xIndex < XPAR_XEMACPS_NUM_INSTANCES ) )
{
NetworkInterface_t * pxInterface = pxMyInterfaces[ xIndex ];
if( pxInterface != NULL )
{
xZynqNetworkInterfaceInitialise( pxInterface );
}
}
}
/*-----------------------------------------------------------*/
static BaseType_t xZynqNetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
{
uint32_t ulLinkSpeed, ulDMAReg;
BaseType_t xStatus, xLinkStatus;
XEmacPs * pxEMAC_PS;
const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL );
NetworkEndPoint_t * pxEndPoint;
BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
configASSERT( xEMACIndex >= 0 );
configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
/* Guard against the init function being called more than once. */
if( xEMACTaskHandles[ xEMACIndex ] == NULL )
{
const char * pcTaskName;
pxMyInterfaces[ xEMACIndex ] = pxInterface;
pxEMAC_PS = &( xEMACpsifs[ xEMACIndex ].emacps );
memset( &xEMACpsifs[ xEMACIndex ], '\0', sizeof( xEMACpsifs[ xEMACIndex ] ) );
xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &( mac_configs[ xEMACIndex ] ), mac_configs[ xEMACIndex ].BaseAddress );
if( xStatus != XST_SUCCESS )
{
FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) );
}
pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
configASSERT( pxEndPoint != NULL );
/* Initialize the mac and set the MAC address at position 1. */
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) pxEndPoint->xMACAddress.ucBytes, 1 );
#if ( ipconfigUSE_LLMNR == 1 )
{
/* Also add LLMNR multicast MAC address. */
#if ( ipconfigUSE_IPv6 == 0 )
{
XEmacPs_SetHash( pxEMAC_PS, ( void * ) xLLMNR_MacAdress.ucBytes );
}
#else
{
NetworkEndPoint_t * pxEndPoint;
NetworkInterface_t * pxInterface = pxMyInterfaces[ xEMACIndex ];
for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
pxEndPoint != NULL;
pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) )
{
if( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED )
{
unsigned char ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
ucMACAddress[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
ucMACAddress[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
ucMACAddress[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
XEmacPs_SetHash( pxEMAC_PS, ( void * ) ucMACAddress );
}
}
XEmacPs_SetHash( pxEMAC_PS, ( void * ) xLLMNR_MacAdressIPv6.ucBytes );
}
#endif /* if ( ipconfigUSE_IPv6 == 0 ) */
}
#endif /* ipconfigUSE_LLMNR == 1 */
#if ( ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_IPv6 != 0 ) )
XEmacPs_SetHash( pxEMAC_PS, ( void * ) xMDNS_MacAdress.ucBytes );
XEmacPs_SetHash( pxEMAC_PS, ( void * ) xMDNS_MACAdressIPv6.ucBytes );
#endif
pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint );
if( pxEndPoint != NULL )
{
/* If there is a second end-point, store the MAC
* address at position 4.*/
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) pxEndPoint->xMACAddress.ucBytes, 4 );
}
/* MDIO goes via ETH0 only */
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 );
{
uint32_t ulValue = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_NWCFG_OFFSET );
/* Allow the use of hashed MAC addresses. */
ulValue |= XEMACPS_NWCFG_MCASTHASHEN_MASK;
#warning As 'MCASTHASHEN' doesn't seem to work, use the promiscuous mode so that IPv6 multicast packets are received.
/* Allow promiscuous mode. */
ulValue |= XEMACPS_NWCFG_COPYALLEN_MASK;
XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_NWCFG_OFFSET, ulValue );
}
/* 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( &( xEMACpsifs[ xEMACIndex ] ) );
init_dma( &( xEMACpsifs[ xEMACIndex ] ) );
start_emacps( &( xEMACpsifs[ xEMACIndex ] ) );
prvGMACWaitLS( xEMACIndex, 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 xEMACTaskHandles[] so interrupts can
* notify the task when there is something to process. */
if( xEMACIndex == 0 )
{
pcTaskName = "GEM0";
}
else
{
pcTaskName = "GEM1";
}
xTaskCreate( prvEMACHandlerTask, pcTaskName, configEMAC_TASK_STACK_SIZE, ( void * ) xEMACIndex, niEMAC_HANDLER_TASK_PRIORITY, &( xEMACTaskHandles[ xEMACIndex ] ) );
}
else
{
/* Initialisation was already performed, just wait for the link. */
prvGMACWaitLS( xEMACIndex, xWaitRelinkDelay );
}
/* Only return pdTRUE when the Link Status of the PHY is high, otherwise the
* DHCP process and all other communication will fail. */
xLinkStatus = xZynqGetPhyLinkStatus( pxInterface );
/* return ( xLinkStatus != pdFALSE ); */
return pdTRUE; /* Workaround because network buffers are not freed when xZynqNetworkInterfaceInitialise() did not complete */
}
/*-----------------------------------------------------------*/
static BaseType_t xZynqNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
NetworkBufferDescriptor_t * const pxBuffer,
BaseType_t bReleaseAfterSend )
{
BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
configASSERT( xEMACIndex >= 0 );
configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
#if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
{
ProtocolPacket_t * pxPacket;
/* If the peripheral must calculate the checksum, it wants
* the protocol checksum to have a value of zero. */
pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer );
#if ( ipconfigUSE_IPv6 != 0 )
ICMPPacket_IPv6_t * pxICMPPacket = ( ICMPPacket_IPv6_t * ) pxBuffer->pucEthernetBuffer;
if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE ) &&
( pxICMPPacket->xIPHeader.ucNextHeader == ipPROTOCOL_ICMP_IPv6 ) )
{
/* The EMAC will calculate the checksum of the IP-header.
* It can only calculate protocol checksums of UDP and TCP,
* so for ICMP and other protocols it must be done manually. */
usGenerateProtocolChecksum( pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength, pdTRUE );
}
#endif
if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) &&
( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP ) )
{
/* The EMAC will calculate the checksum of the IP-header.
* It can only calculate protocol checksums of UDP and TCP,
* so for ICMP and other protocols it must be done manually. */
usGenerateProtocolChecksum( pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength, pdTRUE );
}
}
#endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0UL )
{
iptraceNETWORK_INTERFACE_TRANSMIT();
/* emacps_send_message() will take ownership of pxBuffer, and
* make sure it will get release when bReleaseAfterSend is pdTRUE. */
emacps_send_message( &( xEMACpsifs[ xEMACIndex ] ), pxBuffer, bReleaseAfterSend );
}
else if( bReleaseAfterSend != pdFALSE )
{
/* No link. */
vReleaseNetworkBufferAndDescriptor( pxBuffer );
}
return pdTRUE;
}
/*-----------------------------------------------------------*/
static inline unsigned long ulReadMDIO( BaseType_t xEMACIndex,
unsigned ulRegister )
{
uint16_t usValue;
/* Always ETH0 because both PHYs are connected to ETH0 MDIO */
XEmacPs_PhyRead( &( xEMACpsifs[ 0 ].emacps ), phy_detected[ xEMACIndex ], ulRegister, &usValue );
return usValue;
}
/*-----------------------------------------------------------*/
static BaseType_t prvGMACWaitLS( BaseType_t xEMACIndex,
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;
}
ulPHYLinkStates[ xEMACIndex ] = ulReadMDIO( xEMACIndex, PHY_REG_01_BMSR );
if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0U )
{
xReturn = pdTRUE;
break;
}
vTaskDelay( xShortDelay );
}
return xReturn;
}
/*-----------------------------------------------------------*/
#if ( nicUSE_UNCACHED_MEMORY == 0 )
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;
}
}
#else /* if ( nicUSE_UNCACHED_MEMORY == 0 ) */
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
{
static uint8_t * pucNetworkPackets = NULL;
if( pucNetworkPackets == NULL )
{
pucNetworkPackets = pucGetUncachedMemory( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE );
if( pucNetworkPackets != NULL )
{
uint8_t * ucRAMBuffer = pucNetworkPackets;
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;
}
}
}
}
#endif /* ( nicUSE_UNCACHED_MEMORY == 0 ) */
/*-----------------------------------------------------------*/
static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface )
{
BaseType_t xReturn;
BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) == 0U )
{
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
#if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
/* Do not call the following function directly. It is there for downward compatibility.
* The function FreeRTOS_IPInit() will call it to initialice the interface and end-point
* objects. See the description in FreeRTOS_Routing.h. */
NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
NetworkInterface_t * pxInterface )
{
pxZynq_FillInterfaceDescriptor( xEMACIndex, pxInterface );
}
#endif
/*-----------------------------------------------------------*/
NetworkInterface_t * pxZynq_FillInterfaceDescriptor( BaseType_t xEMACIndex,
NetworkInterface_t * pxInterface )
{
static char pcNames[ XPAR_XEMACPS_NUM_INSTANCES ][ 8 ];
configASSERT( xEMACIndex >= 0 );
configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
/* This function pxZynq_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( pcNames[ xEMACIndex ], sizeof( pcNames[ xEMACIndex ] ), "eth%ld", xEMACIndex );
memset( pxInterface, '\0', sizeof( *pxInterface ) );
pxInterface->pcName = pcNames[ xEMACIndex ]; /* Just for logging, debugging. */
pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
pxInterface->pfInitialise = xZynqNetworkInterfaceInitialise;
pxInterface->pfOutput = xZynqNetworkInterfaceOutput;
pxInterface->pfGetPhyLinkStatus = xZynqGetPhyLinkStatus;
FreeRTOS_AddNetworkInterface( pxInterface );
return pxInterface;
}
/*-----------------------------------------------------------*/
static void prvEMACHandlerTask( void * pvParameters )
{
TimeOut_t xPhyTime;
TickType_t xPhyRemTime;
BaseType_t xResult = 0;
uint32_t xStatus;
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
BaseType_t xEMACIndex = ( BaseType_t ) pvParameters;
xemacpsif_s * pxEMAC_PS;
configASSERT( xEMACIndex >= 0 );
configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
pxEMAC_PS = &( xEMACpsifs[ xEMACIndex ] );
/* 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 );
FreeRTOS_printf( ( "prvEMACHandlerTask[ %ld ] started running\n", xEMACIndex ) );
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 ) */
if( ( pxEMAC_PS->isr_events & EMAC_IF_ALL_EVENT ) == 0 )
{
/* No events to process now, wait for the next. */
ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
}
if( ( pxEMAC_PS->isr_events & EMAC_IF_RX_EVENT ) != 0 )
{
pxEMAC_PS->isr_events &= ~EMAC_IF_RX_EVENT;
xResult = emacps_check_rx( pxEMAC_PS, pxMyInterfaces[ xEMACIndex ] );
}
if( ( pxEMAC_PS->isr_events & EMAC_IF_TX_EVENT ) != 0 )
{
pxEMAC_PS->isr_events &= ~EMAC_IF_TX_EVENT;
emacps_check_tx( pxEMAC_PS );
}
if( ( pxEMAC_PS->isr_events & EMAC_IF_ERR_EVENT ) != 0 )
{
pxEMAC_PS->isr_events &= ~EMAC_IF_ERR_EVENT;
emacps_check_errors( pxEMAC_PS );
}
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;
ulPHYLinkStates[ xEMACIndex ] |= niBMSR_LINK_STATUS;
}
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
{
xStatus = ulReadMDIO( xEMACIndex, PHY_REG_01_BMSR );
if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) )
{
ulPHYLinkStates[ xEMACIndex ] = xStatus;
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0 ) );
}
vTaskSetTimeOutState( &xPhyTime );
if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0 )
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
}
else
{
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
}
}
}
}
/*-----------------------------------------------------------*/