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 "NetworkBufferManagement.h"
44 #include "NetworkInterface.h"
45 
46 /* LPCOpen includes. */
47 #include "chip.h"
48 #include "lpc_phy.h"
49 
50 /* The size of the stack allocated to the task that handles Rx packets. */
51 #define nwRX_TASK_STACK_SIZE    140
52 
53 #if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS )
54     #error please use the new defines with 'ipconfig' prefix
55 #endif
56 
57 #ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS
58 
59 /* Check if the LinkStatus in the PHY is still high after 15 seconds of not
60  * receiving packets. */
61     #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS    15000U
62 #endif
63 
64 #ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS
65     /* Check if the LinkStatus in the PHY is still low every second. */
66     #define ipconfigPHY_LS_LOW_CHECK_TIME_MS    1000U
67 #endif
68 
69 
70 #ifndef configUSE_RMII
71     #define configUSE_RMII    1
72 #endif
73 
74 #ifndef configNUM_RX_DESCRIPTORS
75     #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h
76 #endif
77 
78 #ifndef configNUM_TX_DESCRIPTORS
79     #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h
80 #endif
81 
82 #ifndef NETWORK_IRQHandler
83     #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts.
84 #endif
85 
86 #if !defined( MAC_FF_HMC )
87     /* Hash for multicast. */
88     #define MAC_FF_HMC    ( 1UL << 2UL )
89 #endif
90 
91 #ifndef iptraceEMAC_TASK_STARTING
92     #define iptraceEMAC_TASK_STARTING()    do {} while( ipFALSE_BOOL )
93 #endif
94 
95 /* Define the bits of .STATUS that indicate a reception error. */
96 #define nwRX_STATUS_ERROR_BITS                         \
97     ( RDES_CE /* CRC Error */ |                        \
98       RDES_RE /* Receive Error */ |                    \
99       RDES_DE /* Descriptor Error */ |                 \
100       RDES_RWT /* Receive Watchdog Timeout */ |        \
101       RDES_LC /* Late Collision */ |                   \
102       RDES_OE /* Overflow Error */ |                   \
103       RDES_SAF /* Source Address Filter Fail */ |      \
104       RDES_AFM /* Destination Address Filter Fail */ | \
105       RDES_LE /* Length Error */ )
106 
107 /* Define the EMAC status bits that should trigger an interrupt. */
108 #define nwDMA_INTERRUPT_MASK                               \
109     ( DMA_IE_TIE /* Transmit interrupt enable */ |         \
110       DMA_IE_TSE /* Transmit stopped enable */ |           \
111       DMA_IE_OVE /* Overflow interrupt enable */ |         \
112       DMA_IE_RIE /* Receive interrupt enable */ |          \
113       DMA_IE_NIE /* Normal interrupt summary enable */ |   \
114       DMA_IE_AIE /* Abnormal interrupt summary enable */ | \
115       DMA_IE_RUE /* Receive buffer unavailable enable */ | \
116       DMA_IE_UNE /* Underflow interrupt enable. */ |       \
117       DMA_IE_TJE /* Transmit jabber timeout enable */ |    \
118       DMA_IE_RSE /* Received stopped enable */ |           \
119       DMA_IE_RWE /* Receive watchdog timeout enable */ |   \
120       DMA_IE_FBE ) /* Fatal bus error enable */
121 
122 /* Interrupt events to process.  Currently only the RX/TX events are processed
123  * although code for other events is included to allow for possible future
124  * expansion. */
125 #define EMAC_IF_RX_EVENT     1UL
126 #define EMAC_IF_TX_EVENT     2UL
127 #define EMAC_IF_ERR_EVENT    4UL
128 #define EMAC_IF_ALL_EVENT    ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
129 
130 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
131  * driver will filter incoming packets and only pass the stack those packets it
132  * considers need processing. */
133 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
134     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
135 #else
136     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
137 #endif
138 
139 #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 )
140     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
141         #warning It is adviced to enable both macros
142     #endif
143 #endif
144 
145 #ifndef configPLACE_IN_SECTION_RAM
146     #define configPLACE_IN_SECTION_RAM
147 
148 /*
149  #define configPLACE_IN_SECTION_RAM	__attribute__ ((section(".ramfunc")))
150  */
151 #endif
152 
153 /*-----------------------------------------------------------*/
154 
155 /*
156  * Delay function passed into the library.  The implementation uses FreeRTOS
157  * calls so the scheduler must be started before the driver can be used.
158  */
159 static void prvDelay( uint32_t ulMilliSeconds );
160 
161 /*
162  * Initialises the Tx and Rx descriptors respectively.
163  */
164 static void prvSetupTxDescriptors( void );
165 static void prvSetupRxDescriptors( void );
166 
167 /*
168  * A task that processes received frames.
169  */
170 static void prvEMACHandlerTask( void * pvParameters );
171 
172 /*
173  * Sets up the MAC with the results of an auto-negotiation.
174  */
175 static BaseType_t prvSetLinkSpeed( void );
176 
177 /*
178  * Generates a CRC for a MAC address that is then used to generate a hash index.
179  */
180 static uint32_t prvGenerateCRC32( const uint8_t * ucAddress );
181 
182 /*
183  * Generates a hash index when setting a filter to permit a MAC address.
184  */
185 static uint32_t prvGetHashIndex( const uint8_t * ucAddress );
186 
187 /*
188  * Update the hash table to allow a MAC address.
189  */
190 static void prvAddMACAddress( const uint8_t * ucMacAddress );
191 
192 /*
193  * Sometimes the DMA will report received data as being longer than the actual
194  * received from length.  This function checks the reported length and corrects
195  * if if necessary.
196  */
197 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor );
198 
199 /*-----------------------------------------------------------*/
200 
201 /* Bit map of outstanding ETH interrupt events for processing.  Currently only
202  * the Rx and Tx interrupt is handled, although code is included for other events
203  * to enable future expansion. */
204 static volatile uint32_t ulISREvents;
205 
206 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
207 static uint32_t ulPHYLinkStatus = 0;
208 
209 /* Tx descriptors and index. */
210 static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ];
211 
212 /* ulNextFreeTxDescriptor is declared volatile, because it is accessed from
213  * to different tasks. */
214 static volatile uint32_t ulNextFreeTxDescriptor;
215 static uint32_t ulTxDescriptorToClear;
216 
217 /* Rx descriptors and index. */
218 static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ];
219 static uint32_t ulNextRxDescriptorToProcess;
220 
221 /* The handle of the task that processes Rx packets.  The handle is required so
222  * the task can be notified when new packets arrive. */
223 static TaskHandle_t xRxHanderTask = NULL;
224 
225 #if ( ipconfigUSE_LLMNR == 1 )
226     static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' };
227 #endif /* ipconfigUSE_LLMNR == 1 */
228 
229 /* xTXDescriptorSemaphore is a counting semaphore with
230  * a maximum count of ETH_TXBUFNB, which is the number of
231  * DMA TX descriptors. */
232 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
233 
234 /*-----------------------------------------------------------*/
235 
236 
xNetworkInterfaceInitialise(void)237 BaseType_t xNetworkInterfaceInitialise( void )
238 {
239     BaseType_t xReturn = pdPASS;
240 
241     /* The interrupt will be turned on when a link is established. */
242     NVIC_DisableIRQ( ETHERNET_IRQn );
243 
244     /* Disable receive and transmit DMA processes. */
245     LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR );
246 
247     /* Disable packet reception. */
248     LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE );
249 
250     /* Call the LPCOpen function to initialise the hardware. */
251     Chip_ENET_Init( LPC_ETHERNET );
252 
253     /* Save MAC address. */
254     Chip_ENET_SetADDR( LPC_ETHERNET, ipLOCAL_MAC_ADDRESS );
255 
256     /* Clear all MAC address hash entries. */
257     LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
258     LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
259 
260     #if ( ipconfigUSE_LLMNR == 1 )
261         {
262             prvAddMACAddress( xLLMNR_MACAddress );
263         }
264     #endif /* ipconfigUSE_LLMNR == 1 */
265 
266     /* Promiscuous flag (PR) and Receive All flag (RA) set to zero.  The
267      * registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain
268      * multi-cast addresses. */
269     LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC;
270 
271     #if ( configUSE_RMII == 1 )
272         {
273             if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS )
274             {
275                 xReturn = pdFAIL;
276             }
277         }
278     #else
279         {
280             #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
281                 {
282                     #warning This path has not been tested.
283                 }
284             #endif
285 
286             if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS )
287             {
288                 xReturn = pdFAIL;
289             }
290         }
291     #endif /* if ( configUSE_RMII == 1 ) */
292 
293     if( xReturn == pdPASS )
294     {
295         /* Guard against the task being created more than once and the
296          * descriptors being initialised more than once. */
297         if( xRxHanderTask == NULL )
298         {
299             xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask );
300             configASSERT( xReturn != NULL );
301         }
302 
303         if( xTXDescriptorSemaphore == NULL )
304         {
305             /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS'
306              * and a maximum of 'configNUM_TX_DESCRIPTORS'. */
307             xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS );
308             configASSERT( xTXDescriptorSemaphore != NULL );
309         }
310 
311         /* Enable MAC interrupts. */
312         LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK;
313     }
314 
315     if( xReturn != pdFAIL )
316     {
317         /* Auto-negotiate was already started.  Wait for it to complete. */
318         xReturn = prvSetLinkSpeed();
319 
320         if( xReturn == pdPASS )
321         {
322             /* Initialise the descriptors. */
323             prvSetupTxDescriptors();
324             prvSetupRxDescriptors();
325 
326             /* Clear all interrupts. */
327             LPC_ETHERNET->DMA_STAT = DMA_ST_ALL;
328 
329             /* Enable receive and transmit DMA processes. */
330             LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR;
331 
332             /* Set Receiver / Transmitter Enable. */
333             LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE;
334 
335             /* Start receive polling. */
336             LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
337 
338             /* Enable interrupts in the NVIC. */
339             NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );
340             NVIC_EnableIRQ( ETHERNET_IRQn );
341         }
342     }
343 
344     return xReturn;
345 }
346 /*-----------------------------------------------------------*/
347 
348 #define niBUFFER_1_PACKET_SIZE    1536
349 
350 static __attribute__( ( section( "._ramAHB32" ) ) ) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) );
351 
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])352 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
353 {
354     uint8_t * ucRAMBuffer = ucNetworkPackets;
355     uint32_t ul;
356 
357     for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
358     {
359         pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
360         *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
361         ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
362     }
363 }
364 /*-----------------------------------------------------------*/
365 
366 configPLACE_IN_SECTION_RAM
vClearTXBuffers()367 static void vClearTXBuffers()
368 {
369     uint32_t ulLastDescriptor = ulNextFreeTxDescriptor;
370     size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
371 
372     #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
373         NetworkBufferDescriptor_t * pxNetworkBuffer;
374         uint8_t * ucPayLoad;
375     #endif
376 
377     /* This function is called after a TX-completion interrupt.
378      * It will release each Network Buffer used in xNetworkInterfaceOutput().
379      * 'uxCount' represents the number of descriptors given to DMA for transmission.
380      * After sending a packet, the DMA will clear the 'TDES_OWN' bit. */
381     while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) )
382     {
383         if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) )
384         {
385             break;
386         }
387 
388         #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
389             {
390                 ucPayLoad = ( uint8_t * ) xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD;
391 
392                 if( ucPayLoad != NULL )
393                 {
394                     /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */
395                     pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
396 
397                     configASSERT( pxNetworkBuffer != NULL );
398 
399                     vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
400                     xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t ) 0u;
401                 }
402             }
403         #endif /* ipconfigZERO_COPY_TX_DRIVER */
404 
405         /* Move onto the next descriptor, wrapping if necessary. */
406         ulTxDescriptorToClear++;
407 
408         if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS )
409         {
410             ulTxDescriptorToClear = 0;
411         }
412 
413         uxCount--;
414         /* Tell the counting semaphore that one more TX descriptor is available. */
415         xSemaphoreGive( xTXDescriptorSemaphore );
416     }
417 }
418 
419 /*-----------------------------------------------------------*/
420 
421 configPLACE_IN_SECTION_RAM
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t bReleaseAfterSend)422 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
423                                     BaseType_t bReleaseAfterSend )
424 {
425     BaseType_t xReturn = pdFAIL;
426     const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 );
427 
428     /* Attempt to obtain access to a Tx descriptor. */
429     do
430     {
431         if( xTXDescriptorSemaphore == NULL )
432         {
433             break;
434         }
435 
436         if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
437         {
438             /* Time-out waiting for a free TX descriptor. */
439             break;
440         }
441 
442         /* If the descriptor is still owned by the DMA it can't be used. */
443         if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 )
444         {
445             /* The semaphore was taken, the TX DMA-descriptor is still not available.
446              * Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */
447             xSemaphoreGive( xTXDescriptorSemaphore );
448         }
449         else
450         {
451             #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
452                 {
453                     /* bReleaseAfterSend should always be set when using the zero
454                      * copy driver. */
455                     configASSERT( bReleaseAfterSend != pdFALSE );
456 
457                     /* The DMA's descriptor to point directly to the data in the
458                      * network buffer descriptor.  The data is not copied. */
459                     xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer;
460 
461                     /* The DMA descriptor will 'own' this Network Buffer,
462                      * until it has been sent.  So don't release it now. */
463                     bReleaseAfterSend = false;
464                 }
465             #else /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
466                 {
467                     /* The data is copied from the network buffer descriptor into
468                      * the DMA's descriptor. */
469                     memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
470                 }
471             #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
472 
473             xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength );
474 
475             /* This descriptor is given back to the DMA. */
476             xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN;
477 
478             /* Ensure the DMA is polling Tx descriptors. */
479             LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
480 
481             iptraceNETWORK_INTERFACE_TRANSMIT();
482 
483             /* Move onto the next descriptor, wrapping if necessary. */
484             ulNextFreeTxDescriptor++;
485 
486             if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS )
487             {
488                 ulNextFreeTxDescriptor = 0;
489             }
490 
491             /* The Tx has been initiated. */
492             xReturn = pdPASS;
493         }
494     } while( 0 );
495 
496     /* The buffer has been sent so can be released. */
497     if( bReleaseAfterSend != pdFALSE )
498     {
499         vReleaseNetworkBufferAndDescriptor( pxDescriptor );
500     }
501 
502     return xReturn;
503 }
504 /*-----------------------------------------------------------*/
505 
prvDelay(uint32_t ulMilliSeconds)506 static void prvDelay( uint32_t ulMilliSeconds )
507 {
508     /* Ensure the scheduler was started before attempting to use the scheduler to
509      * create a delay. */
510     configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING );
511 
512     vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) );
513 }
514 /*-----------------------------------------------------------*/
515 
prvSetupTxDescriptors(void)516 static void prvSetupTxDescriptors( void )
517 {
518     BaseType_t x;
519 
520     /* Start with Tx descriptors clear. */
521     memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) );
522 
523     /* Index to the next Tx descriptor to use. */
524     ulNextFreeTxDescriptor = 0ul;
525 
526     /* Index to the next Tx descriptor to clear ( after transmission ). */
527     ulTxDescriptorToClear = 0ul;
528 
529     for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ )
530     {
531         #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
532             {
533                 /* Nothing to do, B1ADD will be set when data is ready to transmit.
534                  * Currently the memset above will have set it to NULL. */
535             }
536         #else
537             {
538                 /* Allocate a buffer to the Tx descriptor.  This is the most basic
539                  * way of creating a driver as the data is then copied into the
540                  * buffer. */
541                 xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
542 
543                 /* Use an assert to check the allocation as +TCP applications will
544                  * often not use a malloc() failed hook as the TCP stack will recover
545                  * from allocation failures. */
546                 configASSERT( xDMATxDescriptors[ x ].B1ADD != 0U );
547             }
548         #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
549 
550         /* Buffers hold an entire frame so all buffers are both the start and
551          * end of a frame. */
552         /* TDES_ENH_TCH     Second Address Chained. */
553         /* TDES_ENH_CIC(n)  Checksum Insertion Control, tried but it does not work for the LPC18xx... */
554         /* TDES_ENH_FS      First Segment. */
555         /* TDES_ENH_LS      Last Segment. */
556         /* TDES_ENH_IC      Interrupt on Completion. */
557         xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC;
558         xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ];
559     }
560 
561     xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER;
562     xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ];
563 
564     /* Point the DMA to the base of the descriptor list. */
565     LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors;
566 }
567 /*-----------------------------------------------------------*/
568 
prvSetupRxDescriptors(void)569 static void prvSetupRxDescriptors( void )
570 {
571     BaseType_t x;
572 
573     #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
574         NetworkBufferDescriptor_t * pxNetworkBuffer;
575     #endif
576 
577     /* Index to the next Rx descriptor to use. */
578     ulNextRxDescriptorToProcess = 0;
579 
580     /* Clear RX descriptor list. */
581     memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) );
582 
583     for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ )
584     {
585         /* Allocate a buffer of the largest	possible frame size as it is not
586          * known what size received frames will be. */
587 
588         #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
589             {
590                 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
591 
592                 /* During start-up there should be enough Network Buffers available,
593                  * so it is safe to use configASSERT().
594                  * In case this assert fails, please check: configNUM_RX_DESCRIPTORS,
595                  * ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c
596                  * is included, check the amount of available heap. */
597                 configASSERT( pxNetworkBuffer != NULL );
598 
599                 /* Pass the actual buffer to DMA. */
600                 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
601             }
602         #else
603             {
604                 /* All DMA descriptors are populated with permanent memory blocks.
605                  * Their contents will be copy to Network Buffers. */
606                 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
607             }
608         #endif /* ipconfigZERO_COPY_RX_DRIVER */
609 
610         /* Use an assert to check the allocation as +TCP applications will often
611          * not use a malloc failed hook as the TCP stack will recover from
612          * allocation failures. */
613         configASSERT( xDMARxDescriptors[ x ].B1ADD != 0U );
614 
615         xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] );
616         xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH;
617 
618         /* The descriptor is available for use by the DMA. */
619         xDMARxDescriptors[ x ].STATUS = RDES_OWN;
620     }
621 
622     /* RDES_ENH_RER  Receive End of Ring. */
623     xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER;
624     xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] );
625 
626     /* Point the DMA to the base of the descriptor list. */
627     LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors;
628 }
629 /*-----------------------------------------------------------*/
630 configPLACE_IN_SECTION_RAM
prvRemoveTrailingBytes(NetworkBufferDescriptor_t * pxDescriptor)631 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor )
632 {
633     size_t xExpectedLength;
634     IPPacket_t * pxIPPacket;
635 
636     pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer;
637     /* Look at the actual length of the packet, translate it to a host-endian notation. */
638     xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength );
639 
640     if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) )
641     {
642         pxDescriptor->xDataLength -= 4;
643     }
644     else
645     {
646         if( pxDescriptor->xDataLength > xExpectedLength )
647         {
648             pxDescriptor->xDataLength = ( size_t ) xExpectedLength;
649         }
650     }
651 }
652 /*-----------------------------------------------------------*/
653 configPLACE_IN_SECTION_RAM
xGetPhyLinkStatus(void)654 BaseType_t xGetPhyLinkStatus( void )
655 {
656     BaseType_t xReturn;
657 
658     if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
659     {
660         xReturn = pdFALSE;
661     }
662     else
663     {
664         xReturn = pdTRUE;
665     }
666 
667     return xReturn;
668 }
669 /*-----------------------------------------------------------*/
670 
671 uint32_t ulDataAvailable;
672 
673 configPLACE_IN_SECTION_RAM
prvNetworkInterfaceInput()674 static BaseType_t prvNetworkInterfaceInput()
675 {
676     BaseType_t xResult = pdFALSE;
677     uint32_t ulStatus;
678     eFrameProcessingResult_t eResult;
679     const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 );
680     const UBaseType_t uxMinimumBuffersRemaining = 3UL;
681     uint16_t usLength;
682     NetworkBufferDescriptor_t * pxDescriptor;
683 
684     #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
685         NetworkBufferDescriptor_t * pxNewDescriptor;
686     #endif /* ipconfigZERO_COPY_RX_DRIVER */
687     #if ( ipconfigUSE_LINKED_RX_MESSAGES == 0 )
688         IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
689     #endif
690 
691     /* Process each descriptor that is not still in use by the DMA. */
692     ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
693 
694     if( ( ulStatus & RDES_OWN ) == 0 )
695     {
696         /* Check packet for errors */
697         if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 )
698         {
699             /* There is some reception error. */
700             intCount[ 3 ]++;
701             /* Clear error bits. */
702             ulStatus &= ~( ( uint32_t ) nwRX_STATUS_ERROR_BITS );
703         }
704         else
705         {
706             xResult++;
707 
708             eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) );
709 
710             if( eResult == eProcessBuffer )
711             {
712                 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
713                 {
714                     ulPHYLinkStatus |= PHY_LINK_CONNECTED;
715                     FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
716                 }
717 
718                 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
719                     if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
720                     {
721                         pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime );
722                     }
723                     else
724                     {
725                         /* Too risky to allocate a new Network Buffer. */
726                         pxNewDescriptor = NULL;
727                     }
728 
729                     if( pxNewDescriptor != NULL )
730                 #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
731                     if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
732                 #endif /* ipconfigZERO_COPY_RX_DRIVER */
733                 {
734                     #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
735                         const uint8_t * pucBuffer;
736                     #endif
737 
738                     /* Get the actual length. */
739                     usLength = RDES_FLMSK( ulStatus );
740 
741                     #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
742                         {
743                             /* Replace the character buffer 'B1ADD'. */
744                             pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD );
745                             xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer;
746 
747                             /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that
748                              * belongs to a Network Buffer.  Find the original Network Buffer. */
749                             pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
750 
751                             /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains
752                              * a reference to a Network Buffer at any time.
753                              * In case it runs out of Network Buffers, a DMA buffer won't be replaced,
754                              * and the received messages is dropped. */
755                             configASSERT( pxDescriptor != NULL );
756                         }
757                     #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
758                         {
759                             /* Create a buffer of exactly the required length. */
760                             pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime );
761                         }
762                     #endif /* ipconfigZERO_COPY_RX_DRIVER */
763 
764                     if( pxDescriptor != NULL )
765                     {
766                         pxDescriptor->xDataLength = ( size_t ) usLength;
767                         #if ( ipconfigZERO_COPY_RX_DRIVER == 0 )
768                             {
769                                 /* Copy the data into the allocated buffer. */
770                                 memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength );
771                             }
772                         #endif /* ipconfigZERO_COPY_RX_DRIVER */
773 
774                         /* It is possible that more data was copied than
775                          * actually makes up the frame.  If this is the case
776                          * adjust the length to remove any trailing bytes. */
777                         prvRemoveTrailingBytes( pxDescriptor );
778 
779                         /* Pass the data to the TCP/IP task for processing. */
780                         xRxEvent.pvData = ( void * ) pxDescriptor;
781 
782                         if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE )
783                         {
784                             /* Could not send the descriptor into the TCP/IP
785                              * stack, it must be released. */
786                             vReleaseNetworkBufferAndDescriptor( pxDescriptor );
787                         }
788                         else
789                         {
790                             iptraceNETWORK_INTERFACE_RECEIVE();
791 
792                             /* The data that was available at the top of this
793                              *  loop has been sent, so is no longer available. */
794                             ulDataAvailable = pdFALSE;
795                         }
796                     }
797                 }
798             }
799             else
800             {
801                 /* The packet is discarded as uninteresting. */
802                 ulDataAvailable = pdFALSE;
803             }
804 
805             /* Got here because received data was sent to the IP task or the
806              * data contained an error and was discarded.  Give the descriptor
807              * back to the DMA. */
808             xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN;
809 
810             /* Move onto the next descriptor. */
811             ulNextRxDescriptorToProcess++;
812 
813             if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS )
814             {
815                 ulNextRxDescriptorToProcess = 0;
816             }
817 
818             ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
819         } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */
820     }     /* if( ( ulStatus & RDES_OWN ) == 0 ) */
821 
822     /* Restart receive polling. */
823     LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
824 
825     return xResult;
826 }
827 /*-----------------------------------------------------------*/
828 
829 configPLACE_IN_SECTION_RAM
NETWORK_IRQHandler(void)830 void NETWORK_IRQHandler( void )
831 {
832     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
833     uint32_t ulDMAStatus;
834     const uint32_t ulRxInterruptMask =
835         DMA_ST_RI | /* Receive interrupt */
836         DMA_ST_RU;  /* Receive buffer unavailable */
837     const uint32_t ulTxInterruptMask =
838         DMA_ST_TI | /* Transmit interrupt */
839         DMA_ST_TPS; /* Transmit process stopped */
840 
841     configASSERT( xRxHanderTask );
842 
843     /* Get pending interrupts. */
844     ulDMAStatus = LPC_ETHERNET->DMA_STAT;
845 
846     /* RX group interrupt(s). */
847     if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 )
848     {
849         /* Remember that an RX event has happened. */
850         ulISREvents |= EMAC_IF_RX_EVENT;
851         vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
852         intCount[ 0 ]++;
853     }
854 
855     /* TX group interrupt(s). */
856     if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 )
857     {
858         /* Remember that a TX event has happened. */
859         ulISREvents |= EMAC_IF_TX_EVENT;
860         vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
861         intCount[ 1 ]++;
862     }
863 
864     /* Test for 'Abnormal interrupt summary'. */
865     if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 )
866     {
867         /* The trace macro must be written such that it can be called from
868          * an interrupt. */
869         iptraceETHERNET_RX_EVENT_LOST();
870     }
871 
872     /* Clear pending interrupts */
873     LPC_ETHERNET->DMA_STAT = ulDMAStatus;
874 
875     /* Context switch needed? */
876     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
877 }
878 /*-----------------------------------------------------------*/
879 
prvSetLinkSpeed(void)880 static BaseType_t prvSetLinkSpeed( void )
881 {
882     BaseType_t xReturn = pdFAIL;
883     TickType_t xTimeOnEntering;
884     uint32_t ulPhyStatus;
885     const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL );
886 
887     /* Ensure polling does not starve lower priority tasks by temporarily
888      * setting the priority of this task to that of the idle task. */
889     vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
890 
891     xTimeOnEntering = xTaskGetTickCount();
892 
893     do
894     {
895         ulPhyStatus = lpcPHYStsPoll();
896 
897         if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 )
898         {
899             /* Set interface speed and duplex. */
900             if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 )
901             {
902                 Chip_ENET_SetSpeed( LPC_ETHERNET, 1 );
903             }
904             else
905             {
906                 Chip_ENET_SetSpeed( LPC_ETHERNET, 0 );
907             }
908 
909             if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 )
910             {
911                 Chip_ENET_SetDuplex( LPC_ETHERNET, true );
912             }
913             else
914             {
915                 Chip_ENET_SetDuplex( LPC_ETHERNET, false );
916             }
917 
918             xReturn = pdPASS;
919             break;
920         }
921     } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay );
922 
923     /* Reset the priority of this task back to its original value. */
924     vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY );
925 
926     return xReturn;
927 }
928 /*-----------------------------------------------------------*/
929 
prvGenerateCRC32(const uint8_t * ucAddress)930 static uint32_t prvGenerateCRC32( const uint8_t * ucAddress )
931 {
932     unsigned int j;
933     const uint32_t Polynomial = 0xEDB88320;
934     uint32_t crc = ~0ul;
935     const uint8_t * pucCurrent = ( const uint8_t * ) ucAddress;
936     const uint8_t * pucLast = pucCurrent + 6;
937 
938     /* Calculate  normal CRC32 */
939     while( pucCurrent < pucLast )
940     {
941         crc ^= *( pucCurrent++ );
942 
943         for( j = 0; j < 8; j++ )
944         {
945             if( ( crc & 1 ) != 0 )
946             {
947                 crc = ( crc >> 1 ) ^ Polynomial;
948             }
949             else
950             {
951                 crc >>= 1;
952             }
953         }
954     }
955 
956     return ~crc;
957 }
958 /*-----------------------------------------------------------*/
959 
prvGetHashIndex(const uint8_t * ucAddress)960 static uint32_t prvGetHashIndex( const uint8_t * ucAddress )
961 {
962     uint32_t ulCrc = prvGenerateCRC32( ucAddress );
963     uint32_t ulIndex = 0ul;
964     BaseType_t xCount = 6;
965 
966     /* Take the lowest 6 bits of the CRC32 and reverse them */
967     while( xCount-- )
968     {
969         ulIndex <<= 1;
970         ulIndex |= ( ulCrc & 1 );
971         ulCrc >>= 1;
972     }
973 
974     /* This is the has value of 'ucAddress' */
975     return ulIndex;
976 }
977 /*-----------------------------------------------------------*/
978 
prvAddMACAddress(const uint8_t * ucMacAddress)979 static void prvAddMACAddress( const uint8_t * ucMacAddress )
980 {
981     BaseType_t xIndex;
982 
983     xIndex = prvGetHashIndex( ucMacAddress );
984 
985     if( xIndex >= 32 )
986     {
987         LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) );
988     }
989     else
990     {
991         LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex );
992     }
993 }
994 /*-----------------------------------------------------------*/
995 
996 configPLACE_IN_SECTION_RAM
prvEMACHandlerTask(void * pvParameters)997 static void prvEMACHandlerTask( void * pvParameters )
998 {
999     TimeOut_t xPhyTime;
1000     TickType_t xPhyRemTime;
1001     UBaseType_t uxLastMinBufferCount = 0;
1002     UBaseType_t uxCurrentCount;
1003     BaseType_t xResult = 0;
1004     uint32_t ulStatus;
1005     const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul );
1006 
1007     /* Remove compiler warning about unused parameter. */
1008     ( void ) pvParameters;
1009 
1010     /* A possibility to set some additional task properties. */
1011     iptraceEMAC_TASK_STARTING();
1012 
1013     vTaskSetTimeOutState( &xPhyTime );
1014     xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
1015 
1016     for( ; ; )
1017     {
1018         uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
1019 
1020         if( uxLastMinBufferCount != uxCurrentCount )
1021         {
1022             /* The logging produced below may be helpful
1023              * while tuning +TCP: see how many buffers are in use. */
1024             uxLastMinBufferCount = uxCurrentCount;
1025             FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
1026                                uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
1027         }
1028 
1029         #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
1030             {
1031                 static UBaseType_t uxLastMinQueueSpace = 0;
1032 
1033                 uxCurrentCount = uxGetMinimumIPQueueSpace();
1034 
1035                 if( uxLastMinQueueSpace != uxCurrentCount )
1036                 {
1037                     /* The logging produced below may be helpful
1038                      * while tuning +TCP: see how many buffers are in use. */
1039                     uxLastMinQueueSpace = uxCurrentCount;
1040                     FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
1041                 }
1042             }
1043         #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
1044 
1045         ulTaskNotifyTake( pdTRUE, xBlockTime );
1046 
1047         xResult = ( BaseType_t ) 0;
1048 
1049         if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
1050         {
1051             /* Code to release TX buffers if zero-copy is used. */
1052             ulISREvents &= ~EMAC_IF_TX_EVENT;
1053             {
1054                 /* Check if DMA packets have been delivered. */
1055                 vClearTXBuffers();
1056             }
1057         }
1058 
1059         if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
1060         {
1061             ulISREvents &= ~EMAC_IF_RX_EVENT;
1062 
1063             xResult = prvNetworkInterfaceInput();
1064 
1065             if( xResult > 0 )
1066             {
1067                 while( prvNetworkInterfaceInput() > 0 )
1068                 {
1069                 }
1070             }
1071         }
1072 
1073         if( xResult > 0 )
1074         {
1075             /* A packet was received. No need to check for the PHY status now,
1076              * but set a timer to check it later on. */
1077             vTaskSetTimeOutState( &xPhyTime );
1078             xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
1079             xResult = 0;
1080         }
1081         else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
1082         {
1083             ulStatus = lpcPHYStsPoll();
1084 
1085             if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) )
1086             {
1087                 ulPHYLinkStatus = ulStatus;
1088                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
1089             }
1090 
1091             vTaskSetTimeOutState( &xPhyTime );
1092 
1093             if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 )
1094             {
1095                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
1096             }
1097             else
1098             {
1099                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
1100             }
1101         }
1102     }
1103 }
1104 /*-----------------------------------------------------------*/
1105