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