433 lines
14 KiB
C
433 lines
14 KiB
C
|
/*
|
||
|
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 "FreeRTOS_ARP.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 niEMAC_HANDLER_TASK_PRIORITY
|
||
|
#define niEMAC_HANDLER_TASK_PRIORITY configMAX_PRIORITIES - 1
|
||
|
#endif
|
||
|
|
||
|
#define niBMSR_LINK_STATUS 0x0004UL
|
||
|
#define niBMSR_AN_COMPLETE 0x0020u /* Auto-Negotiation process completed */
|
||
|
|
||
|
#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, niEMAC_HANDLER_TASK_PRIORITY, &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( xCheckLoopback( pxBuffer, bReleaseAfterSend ) != 0 )
|
||
|
{
|
||
|
/* The packet has been sent back to the IP-task.
|
||
|
The IP-task will further handle it.
|
||
|
Do not release the descriptor. */
|
||
|
return pdTRUE;
|
||
|
}
|
||
|
#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( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP )
|
||
|
{
|
||
|
IPHeader_t *pxIPHeader = &( pxPacket->xUDPPacket.xIPHeader );
|
||
|
|
||
|
pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t )0u;
|
||
|
pxIPHeader->usHeaderChecksum = 0u;
|
||
|
pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );
|
||
|
pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );
|
||
|
|
||
|
usGenerateProtocolChecksum( (uint8_t*)&( pxPacket->xUDPPacket ), pxBuffer->xDataLength, pdTRUE );
|
||
|
}
|
||
|
}
|
||
|
#endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
|
||
|
if( ( ulPHYLinkStatus & niBMSR_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 & niBMSR_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 & niBMSR_LINK_STATUS ) == 0 )
|
||
|
{
|
||
|
xReturn = pdFALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xReturn = pdTRUE;
|
||
|
}
|
||
|
|
||
|
return xReturn;
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|
||
|
static void prvEMACHandlerTask( void *pvParameters )
|
||
|
{
|
||
|
TimeOut_t xPhyTime;
|
||
|
TickType_t xPhyRemTime;
|
||
|
UBaseType_t uxCurrentCount;
|
||
|
BaseType_t xResult = 0;
|
||
|
uint32_t xStatus;
|
||
|
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
|
||
|
UBaseType_t uxLastMinBufferCount = 0;
|
||
|
UBaseType_t uxCurrentBufferCount = 0;
|
||
|
|
||
|
/* 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( ;; )
|
||
|
{
|
||
|
uxCurrentBufferCount = uxGetMinimumFreeNetworkBuffers();
|
||
|
if( uxLastMinBufferCount != uxCurrentBufferCount )
|
||
|
{
|
||
|
/* The logging produced below may be helpful
|
||
|
while tuning +TCP: see how many buffers are in use. */
|
||
|
uxLastMinBufferCount = uxCurrentBufferCount;
|
||
|
FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
|
||
|
uxGetNumberOfFreeNetworkBuffers(), uxCurrentBufferCount ) );
|
||
|
}
|
||
|
|
||
|
#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;
|
||
|
/* Indicate that the Link Status is high, so that
|
||
|
xNetworkInterfaceOutput() can send packets. */
|
||
|
ulPHYLinkStatus |= niBMSR_LINK_STATUS;
|
||
|
}
|
||
|
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
|
||
|
{
|
||
|
xStatus = ulReadMDIO( PHY_REG_01_BMSR );
|
||
|
|
||
|
if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) )
|
||
|
{
|
||
|
ulPHYLinkStatus = xStatus;
|
||
|
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0 ) );
|
||
|
}
|
||
|
|
||
|
vTaskSetTimeOutState( &xPhyTime );
|
||
|
if( ( ulPHYLinkStatus & 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 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*-----------------------------------------------------------*/
|