xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/xilinx_ultrascale/NetworkInterface.c (revision c64bef1957a72e35be2f6584dafea00df0ed6f03)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /* Standard includes. */
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 /* FreeRTOS includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36 #include "queue.h"
37 #include "semphr.h"
38 
39 /* FreeRTOS+TCP includes. */
40 #include "FreeRTOS_IP.h"
41 #include "FreeRTOS_Sockets.h"
42 #include "FreeRTOS_IP_Private.h"
43 #include "FreeRTOS_ARP.h"
44 #include "NetworkBufferManagement.h"
45 #include "NetworkInterface.h"
46 
47 /* Xilinx library files. */
48 #include <xemacps.h>
49 #include "x_topology.h"
50 #include "x_emacpsif.h"
51 #include "x_emacpsif_hw.h"
52 
53 /* Provided memory configured as uncached. */
54 #include "uncached_memory.h"
55 
56 #ifndef niEMAC_HANDLER_TASK_PRIORITY
57     /* Define the priority of the task prvEMACHandlerTask(). */
58     #define niEMAC_HANDLER_TASK_PRIORITY    configMAX_PRIORITIES - 1
59 #endif
60 
61 #define niBMSR_LINK_STATUS                  0x0004uL
62 
63 #if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS )
64     #error please use the new defines with 'ipconfig' prefix
65 #endif
66 
67 #ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS
68 
69 /* Check if the LinkStatus in the PHY is still high after 15 seconds of not
70  * receiving packets. */
71     #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS    15000U
72 #endif
73 
74 #ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS
75     /* Check if the LinkStatus in the PHY is still low every second. */
76     #define ipconfigPHY_LS_LOW_CHECK_TIME_MS    1000U
77 #endif
78 
79 
80 #if ( ipconfigNETWORK_MTU > 1526 )
81     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
82         #warning the use of Jumbo Frames has not been tested sufficiently yet.
83     #endif
84     #define USE_JUMBO_FRAMES    1
85 #endif
86 
87 /* The size of each buffer when BufferAllocation_1 is used:
88  * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */
89 #if ( USE_JUMBO_FRAMES == 1 )
90     #define niBUFFER_1_PACKET_SIZE    10240
91 #else
92     #define niBUFFER_1_PACKET_SIZE    1536
93 #endif
94 
95 /* Naming and numbering of PHY registers. */
96 #define PHY_REG_01_BMSR    0x01         /* Basic mode status register */
97 
98 #ifndef iptraceEMAC_TASK_STARTING
99     #define iptraceEMAC_TASK_STARTING()    do {} while( 0 )
100 #endif
101 
102 /* Default the size of the stack used by the EMAC deferred handler task to twice
103  * the size of the stack used by the idle task - but allow this to be overridden in
104  * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
105 #ifndef configEMAC_TASK_STACK_SIZE
106     #define configEMAC_TASK_STACK_SIZE    ( 8 * configMINIMAL_STACK_SIZE )
107 #endif
108 
109 #if ( ipconfigZERO_COPY_RX_DRIVER == 0 || ipconfigZERO_COPY_TX_DRIVER == 0 )
110     #error Please define both 'ipconfigZERO_COPY_RX_DRIVER' and 'ipconfigZERO_COPY_TX_DRIVER' as 1
111 #endif
112 
113 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 || ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
114     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
115         #warning Please define both 'ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM' and 'ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM' as 1
116     #endif
117 #endif
118 
119 #ifndef nicUSE_UNCACHED_MEMORY
120 
121 /*
122  * Don't use cached memory for network buffer, which is more efficient than
123  * using cached memory.
124  */
125     #define nicUSE_UNCACHED_MEMORY    0
126 #endif
127 
128 /*-----------------------------------------------------------*/
129 
130 /*
131  * Look for the link to be up every few milliseconds until
132  * xMaxTimeTicks has passed or a link is found.
133  */
134 static BaseType_t prvGMACWaitLS( TickType_t xMaxTimeTicks );
135 
136 /*
137  * A deferred interrupt handler for all MAC/DMA interrupt sources.
138  */
139 static void prvEMACHandlerTask( void * pvParameters );
140 
141 /*-----------------------------------------------------------*/
142 
143 /* EMAC data/descriptions. */
144 static xemacpsif_s xEMACpsif;
145 struct xtopology_t xXTopology =
146 {
147     .emac_baseaddr    = XPAR_XEMACPS_0_BASEADDR,
148     .emac_type        = xemac_type_emacps,
149     .intc_baseaddr    = 0x0,
150     .intc_emac_intr   = 0x0,
151     .scugic_baseaddr  = XPAR_SCUGIC_0_CPU_BASEADDR,
152     .scugic_emac_intr = XPAR_XEMACPS_3_INTR,
153 };
154 
155 XEmacPs_Config mac_config =
156 {
157     .DeviceId        = XPAR_PSU_ETHERNET_3_DEVICE_ID, /**< Unique ID  of device */
158     .BaseAddress     = XPAR_PSU_ETHERNET_3_BASEADDR,  /**< Physical base address of IPIF registers */
159     .IsCacheCoherent = XPAR_PSU_ETHERNET_3_IS_CACHE_COHERENT
160 };
161 
162 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
163 static uint32_t ulPHYLinkStatus = 0uL;
164 
165 #if ( ipconfigUSE_LLMNR == 1 )
166     static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
167 #endif
168 
169 /* Holds the handle of the task used as a deferred interrupt processor.  The
170  * handle is used so direct notifications can be sent to the task for all EMAC/DMA
171  * related interrupts. */
172 TaskHandle_t xEMACTaskHandle = NULL;
173 
174 /* The PHY index where a PHY was found. */
175 static u32 ulPHYIndex;
176 
177 /*-----------------------------------------------------------*/
178 
179 /* The function xNetworkInterfaceInitialise() will be called as
180  * long as it returns the value pdFAIL.
181  * It will go through several stages as described in 'eEMACState'.
182  */
183 typedef enum xEMAC_STATE
184 {
185     xEMAC_Init,
186     xEMAC_SetupPHY,
187     xEMAC_WaitPHY,
188     xEMAC_Ready,
189     xEMAC_Fatal,
190 } EMACState_t;
191 
192 static EMACState_t eEMACState = xEMAC_Init;
193 
xNetworkInterfaceInitialise(void)194 BaseType_t xNetworkInterfaceInitialise( void )
195 {
196     uint32_t ulLinkSpeed, ulDMAReg;
197     BaseType_t xStatus, xReturn = pdFAIL;
198     XEmacPs * pxEMAC_PS = &( xEMACpsif.emacps );
199     const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 1000U );
200 
201     switch( eEMACState )
202     {
203         case xEMAC_Init:
204 
205             ulPHYLinkStatus = 0U;
206             memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) );
207 
208             xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress );
209 
210             if( xStatus != XST_SUCCESS )
211             {
212                 FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) );
213                 eEMACState = xEMAC_Fatal;
214                 break;
215             }
216 
217 /* _HT_ : the use of jumbo frames has not been tested sufficiently yet. */
218 
219             if( pxEMAC_PS->Version > 2 )
220             {
221                 #if ( USE_JUMBO_FRAMES == 1 )
222                     /* Enable jumbo frames for zynqmp */
223                     XEmacPs_SetOptions( pxEMAC_PS, XEMACPS_JUMBO_ENABLE_OPTION );
224                 #endif
225             }
226 
227             /* Initialize the mac and set the MAC address. */
228             XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ipLOCAL_MAC_ADDRESS, 1 );
229 
230             #if ( ipconfigUSE_LLMNR == 1 )
231                 {
232                     /* Also add LLMNR multicast MAC address. */
233                     XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) xLLMNR_MACAddress, 2 );
234                 }
235             #endif /* ipconfigUSE_LLMNR == 1 */
236 
237             XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 );
238             ulPHYIndex = ulDetecPHY( pxEMAC_PS );
239 
240             if( ulPHYIndex == ~0U )
241             {
242                 FreeRTOS_printf( ( "xEMACInit: No valid PHY was found\n" ) );
243                 eEMACState = xEMAC_Fatal;
244                 break;
245             }
246 
247             eEMACState = xEMAC_SetupPHY;
248 
249         /* Fall through. */
250 
251         case xEMAC_SetupPHY:
252             ulLinkSpeed = Phy_Setup_US( pxEMAC_PS, ulPHYIndex );
253 
254             if( ulLinkSpeed == XST_FAILURE )
255             {
256                 /* The speed could not be determined yet.
257                  * This is not a fatal error.
258                  * xNetworkInterfaceInitialise() will be called again
259                  * by the IP-task.
260                  */
261                 break;
262             }
263 
264             XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed );
265 
266             /* Setting the operating speed of the MAC needs a delay. */
267             vTaskDelay( pdMS_TO_TICKS( 25UL ) );
268 
269             ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET );
270             /* Enable 16-bytes AHB bursts */
271             ulDMAReg = ulDMAReg | XEMACPS_DMACR_INCR16_AHB_BURST;
272 
273             /* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive
274              * packets from the receiver packet buffer memory when no AHB resource is available. */
275             XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET,
276                               ulDMAReg /*| XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK*/ );
277 
278             setup_isr( &xEMACpsif );
279             init_dma( &xEMACpsif );
280             start_emacps( &xEMACpsif );
281             eEMACState = xEMAC_WaitPHY;
282 
283         /* Fall through. */
284 
285         case xEMAC_WaitPHY:
286             prvGMACWaitLS( xWaitLinkDelay );
287 
288             if( xGetPhyLinkStatus() == pdFALSE )
289             {
290                 /* The Link Status is not yet high, Stay in 'xEMAC_WaitPHY'. */
291                 break;
292             }
293 
294             if( xEMACTaskHandle == NULL )
295             {
296                 /* The deferred interrupt handler task is created at the highest
297                  * possible priority to ensure the interrupt handler can return directly
298                  * to it.  The task's handle is stored in xEMACTaskHandle so interrupts can
299                  * notify the task when there is something to process. */
300                 xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, niEMAC_HANDLER_TASK_PRIORITY, &xEMACTaskHandle );
301 
302                 if( xEMACTaskHandle == NULL )
303                 {
304                     eEMACState = xEMAC_Fatal;
305                     break;
306                 }
307             }
308 
309             eEMACState = xEMAC_Ready;
310 
311         /* Fall through. */
312 
313         case xEMAC_Ready:
314             /* The network driver is operational. */
315             xReturn = pdPASS;
316             break;
317 
318         case xEMAC_Fatal:
319 
320             /* A fatal error has occurred, and the driver
321              * can not start. */
322             break;
323     } /* switch( eEMACState ) */
324 
325     return xReturn;
326 }
327 /*-----------------------------------------------------------*/
328 
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxBuffer,BaseType_t bReleaseAfterSend)329 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer,
330                                     BaseType_t bReleaseAfterSend )
331 {
332     #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
333         {
334             ProtocolPacket_t * pxPacket;
335 
336             /* If the peripheral must calculate the checksum, it wants
337              * the protocol checksum to have a value of zero. */
338             pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer );
339 
340             if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) &&
341                 ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_UDP ) &&
342                 ( pxPacket->xICMPPacket.xIPHeader.ucProtocol != ipPROTOCOL_TCP ) )
343             {
344                 /* The EMAC will calculate the checksum of the IP-header.
345                  * It can only calculate protocol checksums of UDP and TCP,
346                  * so for ICMP and other protocols it must be done manually. */
347                 usGenerateProtocolChecksum( ( uint8_t * ) &( pxPacket->xUDPPacket ), pxBuffer->xDataLength, pdTRUE );
348             }
349         }
350     #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
351 
352     if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0UL )
353     {
354         iptraceNETWORK_INTERFACE_TRANSMIT();
355         emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend );
356     }
357     else if( bReleaseAfterSend != pdFALSE )
358     {
359         /* No link. */
360         vReleaseNetworkBufferAndDescriptor( pxBuffer );
361     }
362 
363     return pdTRUE;
364 }
365 /*-----------------------------------------------------------*/
366 
ulReadMDIO(unsigned ulRegister)367 static inline unsigned long ulReadMDIO( unsigned ulRegister )
368 {
369     uint16_t usValue;
370 
371     XEmacPs_PhyRead( &( xEMACpsif.emacps ), ulPHYIndex, ulRegister, &usValue );
372     return usValue;
373 }
374 /*-----------------------------------------------------------*/
375 
prvGMACWaitLS(TickType_t xMaxTimeTicks)376 static BaseType_t prvGMACWaitLS( TickType_t xMaxTimeTicks )
377 {
378     TickType_t xStartTime, xEndTime;
379     const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL );
380     BaseType_t xReturn;
381 
382     xStartTime = xTaskGetTickCount();
383 
384     for( ; ; )
385     {
386         xEndTime = xTaskGetTickCount();
387 
388         if( xEndTime - xStartTime > xMaxTimeTicks )
389         {
390             xReturn = pdFALSE;
391             break;
392         }
393 
394         ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );
395 
396         if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL )
397         {
398             xReturn = pdTRUE;
399             break;
400         }
401 
402         vTaskDelay( xShortDelay );
403     }
404 
405     return xReturn;
406 }
407 /*-----------------------------------------------------------*/
408 
409 #if ( nicUSE_UNCACHED_MEMORY == 0 )
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])410     void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
411     {
412         static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) );
413         uint8_t * ucRAMBuffer = ucNetworkPackets;
414         uint32_t ul;
415 
416         for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
417         {
418             pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
419             *( ( uintptr_t * ) ucRAMBuffer ) = ( uintptr_t ) &( pxNetworkBuffers[ ul ] );
420             ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
421         }
422     }
423 #else /* if ( nicUSE_UNCACHED_MEMORY == 0 ) */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])424     void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
425     {
426         static uint8_t * pucNetworkPackets = NULL;
427 
428         if( pucNetworkPackets == NULL )
429         {
430             pucNetworkPackets = pucGetUncachedMemory( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE );
431 
432             if( pucNetworkPackets != NULL )
433             {
434                 uint8_t * ucRAMBuffer = pucNetworkPackets;
435                 uint32_t ul;
436 
437                 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
438                 {
439                     pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
440                     *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
441                     ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
442                 }
443             }
444         }
445     }
446 #endif /* ( nicUSE_UNCACHED_MEMORY == 0 ) */
447 /*-----------------------------------------------------------*/
448 
xGetPhyLinkStatus(void)449 BaseType_t xGetPhyLinkStatus( void )
450 {
451     BaseType_t xReturn;
452 
453     if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL )
454     {
455         xReturn = pdFALSE;
456     }
457     else
458     {
459         xReturn = pdTRUE;
460     }
461 
462     return xReturn;
463 }
464 /*-----------------------------------------------------------*/
465 
prvEMACHandlerTask(void * pvParameters)466 static void prvEMACHandlerTask( void * pvParameters )
467 {
468     TimeOut_t xPhyTime;
469     TickType_t xPhyRemTime;
470     BaseType_t xResult = 0;
471     uint32_t xStatus;
472     const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
473 
474     /* Remove compiler warnings about unused parameters. */
475     ( void ) pvParameters;
476 
477     /* A possibility to set some additional task properties like calling
478      * portTASK_USES_FLOATING_POINT() */
479     iptraceEMAC_TASK_STARTING();
480 
481     vTaskSetTimeOutState( &xPhyTime );
482     xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
483 
484     for( ; ; )
485     {
486         #if ( ipconfigHAS_PRINTF != 0 )
487             {
488                 /* Call a function that monitors resources: the amount of free network
489                  * buffers and the amount of free space on the heap.  See FreeRTOS_IP.c
490                  * for more detailed comments. */
491                 vPrintResourceStats();
492             }
493         #endif /* ( ipconfigHAS_PRINTF != 0 ) */
494 
495         if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 )
496         {
497             /* No events to process now, wait for the next. */
498             ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
499         }
500 
501         if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 )
502         {
503             xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT;
504             xResult = emacps_check_rx( &xEMACpsif );
505         }
506 
507         if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 )
508         {
509             xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT;
510             emacps_check_tx( &xEMACpsif );
511         }
512 
513         if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 )
514         {
515             xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT;
516             emacps_check_errors( &xEMACpsif );
517         }
518 
519         if( xResult > 0 )
520         {
521             /* A packet was received. No need to check for the PHY status now,
522              * but set a timer to check it later on. */
523             vTaskSetTimeOutState( &xPhyTime );
524             xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
525             xResult = 0;
526 
527             if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) == 0uL )
528             {
529                 /* Indicate that the Link Status is high, so that
530                  * xNetworkInterfaceOutput() can send packets. */
531                 ulPHYLinkStatus |= niBMSR_LINK_STATUS;
532                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS assume 1\n" ) );
533             }
534         }
535         else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
536         {
537             xStatus = ulReadMDIO( PHY_REG_01_BMSR );
538 
539             if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) )
540             {
541                 ulPHYLinkStatus = xStatus;
542                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL ) );
543             }
544 
545             vTaskSetTimeOutState( &xPhyTime );
546 
547             if( ( ulPHYLinkStatus & niBMSR_LINK_STATUS ) != 0uL )
548             {
549                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
550             }
551             else
552             {
553                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
554             }
555         }
556     }
557 }
558 /*-----------------------------------------------------------*/
559