xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/NXP1060/NetworkInterface.c (revision 245d5879903cfc6e52a466fc1067fe54f869740c)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2023 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  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  */
27 
28 /**
29  * @file NetworkInterface.c
30  * @brief Implements the Network Interface driver for the NXP MIMXRT1060 board.
31  */
32 /* FreeRTOS includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35 #include "queue.h"
36 #include "semphr.h"
37 
38 #include "fsl_device_registers.h"
39 #include "fsl_debug_console.h"
40 #include "board.h"
41 
42 #include "fsl_phy.h"
43 
44 #include "fsl_phyksz8081.h"
45 #include "fsl_enet_mdio.h"
46 #include "fsl_enet.h"
47 
48 /* FreeRTOS+TCP includes. */
49 #include "FreeRTOS_IP.h"
50 #include "FreeRTOS_Routing.h"
51 #include "FreeRTOS_ARP.h"
52 #include "NetworkInterface.h"
53 
54 #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES != 1
55     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
56 #else
57     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
58 #endif
59 
60 /* MDIO operations. */
61 #define EXAMPLE_MDIO_OPS      enet_ops
62 
63 /* PHY operations. */
64 #define EXAMPLE_PHY_OPS       phyksz8081_ops
65 
66 /* ENET clock frequency. */
67 #define EXAMPLE_CLOCK_FREQ    CLOCK_GetFreq( kCLOCK_IpgClk )
68 
69 
70 /*******************************************************************************
71  * Definitions
72  ******************************************************************************/
73 #define ENET_RING_NUM    ( 1 )
74 
75 /* The length or RX buffer. */
76 #ifndef ENET_RXBUFF_SIZE
77     #define ENET_RXBUFF_SIZE    ( ENET_FRAME_MAX_FRAMELEN )
78 #endif
79 
80 /* ENET IRQ priority. Used in FreeRTOS. */
81 /* Interrupt priorities. */
82 #ifdef __CA7_REV
83     #ifndef ENET_PRIORITY
84         #define ENET_PRIORITY         ( 21U )
85     #endif
86     #ifndef ENET_1588_PRIORITY
87         #define ENET_1588_PRIORITY    ( 20U )
88     #endif
89 #else
90     #ifndef ENET_PRIORITY
91         #define ENET_PRIORITY         ( 6U )
92     #endif
93     #ifndef ENET_1588_PRIORITY
94         #define ENET_1588_PRIORITY    ( 5U )
95     #endif
96 #endif /* ifdef __CA7_REV */
97 
98 /* The number of ENET buffers needed to receive frame of maximum length. */
99 #define MAX_BUFFERS_PER_FRAME \
100     ( ( ENET_FRAME_MAX_FRAMELEN / ENET_RXBUFF_SIZE ) + ( ( ENET_FRAME_MAX_FRAMELEN % ENET_RXBUFF_SIZE == 0 ) ? 0 : 1 ) )
101 
102 /* The length or TX buffer. */
103 #ifndef ENET_TXBUFF_SIZE
104     #define ENET_TXBUFF_SIZE    ( ENET_FRAME_MAX_FRAMELEN )
105 #endif
106 
107 /* The number of buffer descriptors in ENET RX ring. */
108 #ifndef ENET_RXBD_NUM
109     #define ENET_RXBD_NUM    ( 5 )
110 #endif
111 
112 /* Ring should be able to receive at least 1 frame with maximum length. */
113 #if ENET_RXBD_NUM < MAX_BUFFERS_PER_FRAME
114     #error "ENET_RXBD_NUM < MAX_BUFFERS_PER_FRAME"
115 #endif
116 
117 /* The number of RX buffers. ENET_RXBD_NUM is always held by ENET driver. */
118 #ifndef ENET_RXBUFF_NUM
119     #define ENET_RXBUFF_NUM    ( ENET_RXBD_NUM * 2 )
120 #endif
121 
122 /* At least ENET_RXBD_NUM number of buffers is always held by ENET driver
123  * for RX. */
124 #if ENET_RXBUFF_NUM < ( ENET_RXBD_NUM + MAX_BUFFERS_PER_FRAME )
125     #error "ENET_RXBUFF_NUM < (ENET_RXBD_NUM + MAX_BUFFERS_PER_FRAME)"
126 #endif
127 
128 /* The number of buffer descriptors in ENET TX ring. */
129 #ifndef ENET_TXBD_NUM
130     #define ENET_TXBD_NUM    ( 3 )
131 #endif
132 
133 /* Set the timeout values such that the total timeout adds up to 4000ms. */
134 #define MAX_AUTONEG_FAILURE_COUNT    ( 40 )
135 #define SINGLE_ITERATION_TIMEOUT     ( 100 )
136 
137 #if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
138     #if defined( FSL_FEATURE_L2CACHE_LINESIZE_BYTE ) && \
139     ( ( !defined( FSL_SDK_DISBLE_L2CACHE_PRESENT ) ) || ( FSL_SDK_DISBLE_L2CACHE_PRESENT == 0 ) )
140         #if defined( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
141             #define FSL_CACHE_LINESIZE_MAX     MAX( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE, FSL_FEATURE_L2CACHE_LINESIZE_BYTE )
142             #define FSL_ENET_BUFF_ALIGNMENT    MAX( ENET_BUFF_ALIGNMENT, FSL_CACHE_LINESIZE_MAX )
143         #else
144             #define FSL_ENET_BUFF_ALIGNMENT    MAX( ENET_BUFF_ALIGNMENT, FSL_FEATURE_L2CACHE_LINESIZE_BYTE )
145         #endif
146     #elif defined( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
147         #define FSL_ENET_BUFF_ALIGNMENT        MAX( ENET_BUFF_ALIGNMENT, FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
148     #else
149         #define FSL_ENET_BUFF_ALIGNMENT        ENET_BUFF_ALIGNMENT
150     #endif /* if defined( FSL_FEATURE_L2CACHE_LINESIZE_BYTE ) && ( ( !defined( FSL_SDK_DISBLE_L2CACHE_PRESENT ) ) || ( FSL_SDK_DISBLE_L2CACHE_PRESENT == 0 ) ) */
151 #else /* if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */
152     #define FSL_ENET_BUFF_ALIGNMENT            ENET_BUFF_ALIGNMENT
153 #endif /* if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */
154 
155 /* A bigger value is chosen so that the previous notification values are
156  * not interfering with the driver ready notifications. */
157 #define DRIVER_READY    ( 0x40 )
158 #define DRIVER_FATAL    ( DRIVER_READY << 1 )
159 
160 #if defined( ENET_ENHANCEDBUFFERDESCRIPTOR_MODE )
161     #error "ENET_ENHANCEDBUFFERDESCRIPTOR_MODE is not supported by this driver"
162 #endif
163 
164 typedef uint8_t   rx_buffer_t[ SDK_SIZEALIGN( ENET_RXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT ) ];
165 typedef uint8_t   tx_buffer_t[ SDK_SIZEALIGN( ENET_TXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT ) ];
166 
167 /**
168  * @brief Used to wrap received data a buffer to be passed into the stack.
169  */
170 typedef struct rx_pbuf_wrapper
171 {
172     void * buffer;             /*!< Original buffer wrapped. */
173     volatile bool buffer_used; /*!< Wrapped buffer is used by ENET or FreeRTOS+TCP. */
174 } rx_pbuf_wrapper_t;
175 
176 /**
177  * Helper structure to hold private data used to operate your Ethernet interface.
178  */
179 struct ethernetif
180 {
181     ENET_Type * base;
182     enet_handle_t handle;
183     enet_rx_bd_struct_t * RxBuffDescrip;
184     enet_tx_bd_struct_t * TxBuffDescrip;
185     rx_buffer_t * RxDataBuff;
186     tx_buffer_t * TxDataBuff;
187     rx_pbuf_wrapper_t RxPbufs[ ENET_RXBUFF_NUM ];
188 };
189 
190 typedef enum xEMAC_STATE
191 {
192     xEMAC_SetupPHY,
193     xEMAC_WaitPHY,
194     xEMAC_Init,
195     xEMAC_Ready,
196 } EMACState_t;
197 
198 static EMACState_t eEMACState = xEMAC_SetupPHY;
199 
200 static mdio_handle_t mdioHandle = { .ops = &EXAMPLE_MDIO_OPS };
201 
202 static phy_handle_t phyHandle = { .phyAddr = 0x00, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS };
203 
204 /**
205  * The task-handle for deferred interrupt handler task that processes
206  * incoming packets.
207  */
208 static TaskHandle_t receiveTaskHandle = NULL;
209 
210 static struct ethernetif EthernetInterface1;
211 static struct ethernetif * ethernetifLocal = &EthernetInterface1;
212 
213 static bool bGlobalLinkStatus = false;
214 
215 static NetworkInterface_t * pxMyInterface = NULL;
216 
217 /*-----------------------------------------------------------*/
218 
219 AT_NONCACHEABLE_SECTION_ALIGN( static enet_rx_bd_struct_t rxBuffDescrip_0[ ENET_RXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
220 AT_NONCACHEABLE_SECTION_ALIGN( static enet_tx_bd_struct_t txBuffDescrip_0[ ENET_TXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
221 SDK_ALIGN( static rx_buffer_t rxDataBuff_0[ ENET_RXBUFF_NUM ], FSL_ENET_BUFF_ALIGNMENT );
222 SDK_ALIGN( static tx_buffer_t txDataBuff_0[ ENET_TXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
223 
224 /*-----------------------------------------------------------*/
225 /*-----------------------------------------------------------*/
226 /*------------ PHY configuration parameters. ----------------*/
227 static phy_config_t xConfig =
228 {
229     .autoNeg   = pdTRUE,                  /* Allow auto-negotiation. */
230     .duplex    = kPHY_FullDuplex,         /* Use full duplex mode. In case
231                                            * auto-negotiation is turned on,
232                                            * this is not used. */
233     .phyAddr   = BOARD_ENET0_PHY_ADDRESS, /* The PHY address. */
234     .speed     = kPHY_Speed100M,          /* Use 100 Mbps configuration (maximum possible
235                                            * for this PHY). In case auto-negotiation is
236                                            * turned on, this is not used. */
237     .enableEEE = pdFALSE                  /* Disable the energy efficient PHY. */
238 };
239 
240 /*-----------------------------------------------------------*/
241 /*-----------------------------------------------------------*/
242 
243 static void prvEMACHandlerTask( void * pvParameters );
244 
245 static void ethernet_callback( ENET_Type * base,
246                                enet_handle_t * handle,
247                                enet_event_t event,
248                                enet_frame_info_t * frameInfo,
249                                void * userData );
250 
251 static void prvProcessFrame( int length );
252 
253 static status_t xSetupPHY( phy_config_t * pxConfig );
254 
255 static status_t xWaitPHY( phy_config_t xConfig );
256 
257 static status_t xEMACInit( phy_speed_t speed,
258                            phy_duplex_t duplex );
259 
260 static BaseType_t prvNXP1060_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
261 
262 static BaseType_t prvNXP1060_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
263                                                      NetworkBufferDescriptor_t * const pxNetworkBuffer,
264                                                      BaseType_t xReleaseAfterSend );
265 
266 static BaseType_t prvNXP1060_GetPhyLinkStatus( NetworkInterface_t * pxInterface );
267 
268 NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex,
269                                                         NetworkInterface_t * pxInterface );
270 /*-----------------------------------------------------------*/
271 
272 #if ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 )
273 
274 /* Do not call the following function directly. It is there for downward compatibility.
275  * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point
276  * objects.  See the description in FreeRTOS_Routing.h. */
pxFillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)277     NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
278                                                     NetworkInterface_t * pxInterface )
279     {
280         pxNXP1060_FillInterfaceDescriptor( xEMACIndex, pxInterface );
281     }
282 
283 #endif /* ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) */
284 /*-----------------------------------------------------------*/
285 
pxNXP1060_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)286 NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex,
287                                                         NetworkInterface_t * pxInterface )
288 {
289     static char pcName[ 10 ];
290 
291     /* This function pxNXP1060_FillInterfaceDescriptor() adds a network-interface.
292      * Make sure that the object pointed to by 'pxInterface'
293      * is declared static or global, and that it will remain to exist. */
294 
295     snprintf( pcName, sizeof( pcName ), "NXP1060%ld", xEMACIndex );
296 
297     memset( pxInterface, '\0', sizeof( *pxInterface ) );
298     pxInterface->pcName = pcName;                    /* Just for logging, debugging. */
299     pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
300     pxInterface->pfInitialise = prvNXP1060_NetworkInterfaceInitialise;
301     pxInterface->pfOutput = prvNXP1060_NetworkInterfaceOutput;
302     pxInterface->pfGetPhyLinkStatus = prvNXP1060_GetPhyLinkStatus;
303 
304     FreeRTOS_AddNetworkInterface( pxInterface );
305     pxMyInterface = pxInterface;
306 
307     return pxInterface;
308 }
309 /*-----------------------------------------------------------*/
310 
prvNXP1060_NetworkInterfaceInitialise(NetworkInterface_t * pxInterface)311 static BaseType_t prvNXP1060_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
312 {
313     status_t xStatus;
314     BaseType_t xResult = pdFAIL;
315     phy_speed_t speed;
316     phy_duplex_t duplex;
317     BaseType_t xTaskCreated;
318     static BaseType_t xFirstCall = pdTRUE;
319 
320     configASSERT( FSL_FEATURE_ENET_QUEUE == 1 );
321 
322     switch( eEMACState )
323     {
324         case xEMAC_SetupPHY:
325             xStatus = xSetupPHY( &xConfig );
326 
327             if( xStatus != kStatus_Success )
328             {
329                 break;
330             }
331             else
332             {
333                 eEMACState = xEMAC_WaitPHY;
334             }
335 
336         /* Fall through. */
337         case xEMAC_WaitPHY:
338             FreeRTOS_printf( ( "Configuration successful. Waiting for link to go up"
339                                " and auto-negotiation to complete." ) );
340 
341             xStatus = xWaitPHY( xConfig );
342 
343             if( xStatus == kStatus_Success )
344             {
345                 xStatus = PHY_GetLinkSpeedDuplex( &phyHandle, &speed, &duplex );
346             }
347 
348             if( xStatus != kStatus_Success )
349             {
350                 break;
351             }
352             else
353             {
354                 eEMACState = xEMAC_Init;
355             }
356 
357         /* Fall through. */
358         case xEMAC_Init:
359             xStatus = xEMACInit( speed, duplex );
360 
361             if( ( xFirstCall == pdTRUE ) || ( receiveTaskHandle == NULL ) )
362             {
363                 if( xStatus == kStatus_Success )
364                 {
365                     /* The link is now up. */
366                     bGlobalLinkStatus = true;
367 
368                     /* The handler task is created at the highest possible priority to
369                      * ensure the interrupt handler can return directly to it. */
370                     xTaskCreated = xTaskCreate( prvEMACHandlerTask,
371                                                 "EMAC-Handler",
372                                                 configMINIMAL_STACK_SIZE * 3,
373                                                 NULL,
374                                                 configMAX_PRIORITIES - 1,
375                                                 &receiveTaskHandle );
376 
377                     if( ( receiveTaskHandle == NULL ) || ( xTaskCreated != pdPASS ) )
378                     {
379                         FreeRTOS_printf( ( "Failed to create the handler task." ) );
380                         break;
381                     }
382 
383                     /* Enable the interrupt and set its priority to the minimum
384                      * interrupt priority.  */
385                     NVIC_SetPriority( ENET_IRQn, ENET_PRIORITY );
386                     NVIC_EnableIRQ( ENET_IRQn );
387 
388                     eEMACState = xEMAC_Ready;
389 
390                     /* After this, the task should not be created. */
391                     xFirstCall = pdFALSE;
392                 }
393                 else
394                 {
395                     break;
396                 }
397             }
398             else
399             {
400                 eEMACState = xEMAC_Ready;
401             }
402 
403         /* Fall through. */
404         case xEMAC_Ready:
405             FreeRTOS_printf( ( "Driver ready for use." ) );
406 
407             /* Kick the task once the driver is ready. */
408             if( receiveTaskHandle != NULL )
409             {
410                 xTaskNotify( receiveTaskHandle, DRIVER_READY, eSetValueWithOverwrite );
411             }
412 
413             xResult = pdPASS;
414 
415             break;
416     }
417 
418     return xResult;
419 }
420 /*-----------------------------------------------------------*/
421 
prvNXP1060_NetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)422 static BaseType_t prvNXP1060_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
423                                                      NetworkBufferDescriptor_t * const pxNetworkBuffer,
424                                                      BaseType_t xReleaseAfterSend )
425 {
426     status_t result;
427     BaseType_t xReturn = pdFAIL;
428 
429     /* Avoid warning about unused parameter. */
430     ( void ) pxInterface;
431 
432     do
433     {
434         if( xCheckLoopback( pxNetworkBuffer, xReleaseAfterSend ) != 0 )
435         {
436             /* The packet has been sent back to the IP-task.
437              * The IP-task will further handle it.
438              * Do not release the descriptor. */
439             xReleaseAfterSend = pdFALSE;
440             break;
441         }
442 
443         if( bGlobalLinkStatus == true )
444         {
445             /* ENET_SendFrame copies the data before sending it. Therefore, the network buffer can
446              * be released without worrying about the buffer memory being used by the ENET_SendFrame
447              * function. */
448             result = ENET_SendFrame( ethernetifLocal->base,
449                                      &ethernetifLocal->handle,
450                                      pxNetworkBuffer->pucEthernetBuffer,
451                                      pxNetworkBuffer->xDataLength,
452                                      0,
453                                      false,
454                                      NULL );
455 
456             switch( result )
457             {
458                 case kStatus_ENET_TxFrameBusy:
459                     FreeRTOS_printf( ( "Failed to send the frame - driver busy!" ) );
460                     break;
461 
462                 case kStatus_Success:
463                     iptraceNETWORK_INTERFACE_TRANSMIT();
464                     xReturn = pdPASS;
465                     break;
466             }
467         }
468     } while( ipFALSE_BOOL );
469 
470     if( xReleaseAfterSend == pdTRUE )
471     {
472         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
473     }
474 
475     return xReturn;
476 }
477 /*-----------------------------------------------------------*/
478 
prvNXP1060_GetPhyLinkStatus(NetworkInterface_t * pxInterface)479 static BaseType_t prvNXP1060_GetPhyLinkStatus( NetworkInterface_t * pxInterface )
480 {
481     BaseType_t xReturn = pdFALSE;
482 
483     /* Avoid warning about unused parameter. */
484     ( void ) pxInterface;
485 
486     if( bGlobalLinkStatus == true )
487     {
488         xReturn = pdTRUE;
489     }
490 
491     return xReturn;
492 }
493 /*-----------------------------------------------------------*/
494 
prvEMACHandlerTask(void * parameter)495 static void prvEMACHandlerTask( void * parameter )
496 {
497     bool bLinkUp = false;
498     status_t readStatus;
499 
500     /* Wait for the driver to finish starting. */
501     ( void ) ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
502 
503     while( pdTRUE )
504     {
505         if( ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( 500 ) ) == pdFALSE )
506         {
507             /* No RX packets for a bit so check for a link. */
508             const IPStackEvent_t xNetworkEventDown = { .eEventType = eNetworkDownEvent, .pvData = NULL };
509 
510             do
511             {
512                 readStatus = PHY_GetLinkStatus( &phyHandle, &bLinkUp );
513 
514                 if( readStatus == kStatus_Success )
515                 {
516                     if( bLinkUp == pdFALSE )
517                     {
518                         /* The link is down. */
519                         bGlobalLinkStatus = false;
520                         /* We need to setup the PHY again. */
521                         eEMACState = xEMAC_WaitPHY;
522 
523                         FreeRTOS_printf( ( "Link down!" ) );
524 
525                         xSendEventStructToIPTask( &xNetworkEventDown, 0U );
526 
527                         /* Wait for the driver to finish initialization. */
528                         uint32_t ulNotificationValue;
529 
530                         do
531                         {
532                             ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
533                         } while( !( ulNotificationValue & DRIVER_READY ) );
534                     }
535                     else
536                     {
537                         /* The link is still up. */
538                         bGlobalLinkStatus = true;
539                     }
540                 }
541             } while( bGlobalLinkStatus == false );
542         }
543         else
544         {
545             BaseType_t receiving = pdTRUE;
546 
547             /* A packet was received, the link must be up. */
548             bGlobalLinkStatus = true;
549 
550             while( receiving == pdTRUE )
551             {
552                 uint32_t length;
553                 const status_t status = ENET_GetRxFrameSize( &( ethernetifLocal->handle ), &length, 0 );
554 
555                 switch( status )
556                 {
557                     case kStatus_Success: /* there is a frame.  process it */
558 
559                         if( length )
560                         {
561                             prvProcessFrame( length );
562                         }
563 
564                         break;
565 
566                     case kStatus_ENET_RxFrameEmpty: /* Received an empty frame.  Ignore it */
567                         receiving = pdFALSE;
568                         break;
569 
570                     case kStatus_ENET_RxFrameError: /* Received an error frame.  Read & drop it */
571                         PRINTF( "RX Receive Error\n" );
572                         ENET_ReadFrame( ethernetifLocal->base, &( ethernetifLocal->handle ), NULL, 0, 0, NULL );
573                         /* Not sure if a trace is required.  The MAC had an error and needed to dump bytes */
574                         break;
575 
576                     default:
577                         PRINTF( "RX Receive default" );
578                         break;
579                 }
580             }
581         }
582     }
583 }
584 /*-----------------------------------------------------------*/
585 
586 /**
587  * @brief Callback for ENET interrupts. We have only enabled the Ethernet receive interrupts
588  * in the case of this driver.
589  */
ethernet_callback(ENET_Type * base,enet_handle_t * handle,enet_event_t event,enet_frame_info_t * frameInfo,void * userData)590 static void ethernet_callback( ENET_Type * base,
591                                enet_handle_t * handle,
592                                enet_event_t event,
593                                enet_frame_info_t * frameInfo,
594                                void * userData )
595 {
596     BaseType_t needsToYield = pdFALSE;
597 
598     ( void ) base;
599     ( void ) handle;
600     ( void ) frameInfo;
601     ( void ) userData;
602 
603     switch( event )
604     {
605         case kENET_RxEvent:
606             vTaskNotifyGiveFromISR( receiveTaskHandle, &needsToYield );
607             portEND_SWITCHING_ISR( needsToYield );
608             break;
609 
610         default:
611             FreeRTOS_printf( ( "Unknown interrupt callback %u!", event ) );
612             break;
613     }
614 }
615 /*-----------------------------------------------------------*/
616 
617 /**
618  * @brief This function verifies that the incoming frame needs processing.
619  *        If the frame is deemed to be appropriate, then the frame is sent to the
620  *        TCP stack for further processing.
621  * @param[in] length: The length of the incoming frame. This length should be read
622  *                    using a call to ENET_GetRxFrameSize.
623  */
prvProcessFrame(int length)624 static void prvProcessFrame( int length )
625 {
626     NetworkBufferDescriptor_t * pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( length, 0 );
627     BaseType_t xRelease = pdFALSE;
628 
629     if( pxBufferDescriptor != NULL )
630     {
631         ENET_ReadFrame( ethernetifLocal->base, &( ethernetifLocal->handle ), pxBufferDescriptor->pucEthernetBuffer, length, 0, NULL );
632         pxBufferDescriptor->xDataLength = length;
633         pxBufferDescriptor->pxInterface = pxMyInterface;
634         pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxBufferDescriptor->pucEthernetBuffer );
635 
636         if( pxBufferDescriptor->pxEndPoint == NULL )
637         {
638             /* Endpoint not found, drop the packet. */
639             xRelease = pdTRUE;
640         }
641         else
642         {
643             if( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer )
644             {
645                 IPStackEvent_t xRxEvent;
646                 xRxEvent.eEventType = eNetworkRxEvent;
647                 xRxEvent.pvData = ( void * ) pxBufferDescriptor;
648 
649                 if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
650                 {
651                     xRelease = pdTRUE;
652                     iptraceETHERNET_RX_EVENT_LOST();
653                     FreeRTOS_printf( ( "RX Event Lost\n" ) );
654                 }
655             }
656             else
657             {
658                 #if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) )
659                     const EthernetHeader_t * pxEthernetHeader;
660                     char ucSource[ 18 ];
661                     char ucDestination[ 18 ];
662 
663                     pxEthernetHeader = ( ( const EthernetHeader_t * ) pxBufferDescriptor->pucEthernetBuffer );
664 
665                     FreeRTOS_EUI48_ntop( pxEthernetHeader->xSourceAddress.ucBytes, ucSource, 'A', ':' );
666                     FreeRTOS_EUI48_ntop( pxEthernetHeader->xDestinationAddress.ucBytes, ucDestination, 'A', ':' );
667 
668                     FreeRTOS_debug_printf( ( "Invalid target MAC: dropping frame from: %s to: %s", ucSource, ucDestination ) );
669                 #endif /* if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) ) */
670                 xRelease = pdTRUE;
671                 /* Not sure if a trace is required.  The stack did not want this message */
672             }
673         }
674 
675         if( xRelease != pdFALSE )
676         {
677             vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
678         }
679     }
680     else
681     {
682         #if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) )
683             FreeRTOS_debug_printf( ( "No Buffer Available: dropping incoming frame!!" ) );
684         #endif
685         ENET_ReadFrame( ENET, &( ethernetifLocal->handle ), NULL, length, 0, NULL );
686 
687         /* No buffer available to receive this message */
688         iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
689     }
690 }
691 /*-----------------------------------------------------------*/
692 
693 /**
694  * @brief This function is used to setup the PHY in auto-negotiation mode.
695  *
696  * @param[out] pxConfig: the configuration parameters.
697  *
698  * @return kStatus_Success if the PHY was initialized; error code otherwise.
699  */
xSetupPHY(phy_config_t * pxConfig)700 static status_t xSetupPHY( phy_config_t * pxConfig )
701 {
702     status_t xStatus;
703 
704     /* Set the clock frequency. MDIO handle is pointed to by the PHY handle. */
705     mdioHandle.resource.csrClock_Hz = EXAMPLE_CLOCK_FREQ;
706     mdioHandle.resource.base = ( void * ) ENET_BASE;
707 
708     FreeRTOS_printf( ( "Starting PHY initialization." ) );
709 
710     xStatus = PHY_Init( &phyHandle, pxConfig );
711 
712     if( xStatus != kStatus_Success )
713     {
714         FreeRTOS_printf( ( "Failed to initialize the PHY." ) );
715     }
716 
717     return xStatus;
718 }
719 /*-----------------------------------------------------------*/
720 
721 /**
722  * @brief This function is used wait on the auto-negotiation completion.
723  *
724  * @param[in] xConfig: the configuration parameters.
725  *
726  * @return kStatus_Success if the PHY was initialized; error code otherwise.
727  */
xWaitPHY(phy_config_t xConfig)728 static status_t xWaitPHY( phy_config_t xConfig )
729 {
730     status_t xStatus;
731     bool bLinkUp;
732     bool bAutoNegotiationComplete;
733     uint8_t ucCounter = 0;
734 
735     do
736     {
737         xStatus = PHY_GetLinkStatus( &phyHandle, &bLinkUp );
738 
739         if( bLinkUp == true )
740         {
741             break;
742         }
743 
744         /* Try for only a limited number of times. */
745         if( ucCounter++ > MAX_AUTONEG_FAILURE_COUNT )
746         {
747             break;
748         }
749 
750         vTaskDelay( pdMS_TO_TICKS( SINGLE_ITERATION_TIMEOUT ) );
751     }
752     while( xStatus == kStatus_Success );
753 
754     if( bLinkUp == false )
755     {
756         FreeRTOS_printf( ( "Failed to get the link up." ) );
757         xStatus = kStatus_Fail;
758     }
759     else
760     {
761         FreeRTOS_printf( ( "Link up." ) );
762     }
763 
764     if( ( xStatus == kStatus_Success ) &&
765         ( bLinkUp == true ) &&
766         ( xConfig.autoNeg == true ) )
767     {
768         /* Reset the counter for next use. */
769         ucCounter = 0;
770 
771         FreeRTOS_printf( ( "Waiting for auto-negotiation to complete." ) );
772 
773         do
774         {
775             xStatus = PHY_GetAutoNegotiationStatus( &phyHandle, &bAutoNegotiationComplete );
776 
777             if( bAutoNegotiationComplete == true )
778             {
779                 break;
780             }
781 
782             /* Try for only a limited number of times. */
783             if( ucCounter++ > MAX_AUTONEG_FAILURE_COUNT )
784             {
785                 break;
786             }
787 
788             vTaskDelay( pdMS_TO_TICKS( SINGLE_ITERATION_TIMEOUT ) );
789         }
790         while( xStatus == kStatus_Success );
791 
792         if( bAutoNegotiationComplete == false )
793         {
794             FreeRTOS_printf( ( "Failed to complete auto-negotiation." ) );
795             xStatus = kStatus_Fail;
796         }
797         else
798         {
799             /* Success in auto-negotiation and the link is up. */
800             FreeRTOS_printf( ( "Auto-negotiation complete." ) );
801         }
802     }
803 
804     return xStatus;
805 }
806 /*-----------------------------------------------------------*/
807 
808 /**
809  * @brief This function is used to initialize the ENET module. It initializes the network buffers
810  *        and buffer descriptors.
811  *
812  * @param[in] speed: The speed of communication (either set by auto-negotiation or the default
813  *                   value).
814  * @param[in] duplex: The nature of the channel. This must be set to kPHY_FullDuplex by
815  *                   auto-negotiation.
816  *
817  * @return kStatus_Success if the ENET module was initialized; error code otherwise.
818  */
xEMACInit(phy_speed_t speed,phy_duplex_t duplex)819 static status_t xEMACInit( phy_speed_t speed,
820                            phy_duplex_t duplex )
821 {
822     enet_config_t config;
823     uint32_t sysClock;
824     enet_buffer_config_t buffCfg[ ENET_RING_NUM ];
825     status_t xStatus;
826     uint32_t instance;
827     static ENET_Type * const enetBases[] = ENET_BASE_PTRS;
828     static const IRQn_Type enetTxIrqId[] = ENET_Transmit_IRQS;
829     /*! @brief Pointers to enet receive IRQ number for each instance. */
830     static const IRQn_Type enetRxIrqId[] = ENET_Receive_IRQS;
831     int i;
832     NetworkEndPoint_t * pxEndPoint;
833 
834     ethernetifLocal->RxBuffDescrip = &( rxBuffDescrip_0[ 0 ] );
835     ethernetifLocal->TxBuffDescrip = &( txBuffDescrip_0[ 0 ] );
836     ethernetifLocal->RxDataBuff = &( rxDataBuff_0[ 0 ] );
837     ethernetifLocal->TxDataBuff = &( txDataBuff_0[ 0 ] );
838     ethernetifLocal->base = ( void * ) ENET_BASE;
839 
840     /* prepare the buffer configuration. */
841     buffCfg[ 0 ].rxBdNumber = ENET_RXBD_NUM;                  /* Number of RX buffer descriptors. */
842     buffCfg[ 0 ].txBdNumber = ENET_TXBD_NUM;                  /* Transmit buffer descriptor number. */
843     buffCfg[ 0 ].rxBuffSizeAlign = sizeof( rx_buffer_t );     /* Aligned receive data buffer size. */
844     buffCfg[ 0 ].txBuffSizeAlign = sizeof( tx_buffer_t );     /* Aligned transmit data buffer size. */
845     buffCfg[ 0 ].rxBdStartAddrAlign =
846         &( rxBuffDescrip_0[ 0 ] );                            /* Aligned receive buffer descriptor start address. */
847     buffCfg[ 0 ].txBdStartAddrAlign =
848         &( txBuffDescrip_0[ 0 ] );                            /* Aligned transmit buffer descriptor start address. */
849     buffCfg[ 0 ].rxBufferAlign =
850         &( rxDataBuff_0[ 0 ][ 0 ] );                          /* Receive data buffer start address. */
851     buffCfg[ 0 ].txBufferAlign = &( txDataBuff_0[ 0 ][ 0 ] ); /* Transmit data buffer start address. */
852     buffCfg[ 0 ].txFrameInfo = NULL;                          /* Transmit frame information start address. Set only if using zero-copy transmit. */
853     buffCfg[ 0 ].rxMaintainEnable = true;                     /* Receive buffer cache maintain. */
854     buffCfg[ 0 ].txMaintainEnable = true;                     /* Transmit buffer cache maintain. */
855 
856     sysClock = phyHandle.mdioHandle->resource.csrClock_Hz;
857 
858     ENET_GetDefaultConfig( &config );
859 
860     config.ringNum = ENET_RING_NUM;
861     config.rxBuffAlloc = NULL;
862     config.rxBuffFree = NULL;
863     config.userData = ethernetifLocal;
864     config.miiSpeed = ( enet_mii_speed_t ) speed;
865     config.miiDuplex = ( enet_mii_duplex_t ) duplex;
866 
867     /* Only get interrupt for incoming messages. */
868     config.interrupt = kENET_RxFrameInterrupt;
869     config.callback = ethernet_callback;
870 
871     for( instance = 0; instance < ARRAY_SIZE( enetBases ); instance++ )
872     {
873         if( enetBases[ instance ] == ethernetifLocal->base )
874         {
875             NVIC_SetPriority( enetRxIrqId[ instance ], ENET_PRIORITY );
876             NVIC_SetPriority( enetTxIrqId[ instance ], ENET_PRIORITY );
877             break;
878         }
879     }
880 
881     configASSERT( instance != ARRAY_SIZE( enetBases ) );
882 
883     if( instance == ARRAY_SIZE( enetBases ) )
884     {
885         xStatus = kStatus_Fail;
886     }
887     else
888     {
889         pxEndPoint = FreeRTOS_FirstEndPoint( pxMyInterface );
890         configASSERT( pxEndPoint != NULL );
891 
892         for( i = 0; i < ENET_RXBUFF_NUM; i++ )
893         {
894             ethernetifLocal->RxPbufs[ i ].buffer = &( ethernetifLocal->RxDataBuff[ i ][ 0 ] );
895             ethernetifLocal->RxPbufs[ i ].buffer_used = false;
896         }
897 
898         /* Initialize the ENET module. */
899         xStatus = ENET_Init( ethernetifLocal->base,
900                              &ethernetifLocal->handle,
901                              &config,
902                              &buffCfg[ 0 ],
903                              pxEndPoint->xMACAddress.ucBytes,
904                              sysClock );
905 
906         #if ( ipconfigUSE_LLMNR == 1 )
907             ENET_AddMulticastGroup( ethernetifLocal->base, ( uint8_t * ) xLLMNR_MacAdress.ucBytes );
908         #endif /* ipconfigUSE_LLMNR */
909 
910         #if ( ipconfigUSE_IPv6 != 0 )
911             #if ( ipconfigUSE_LLMNR == 1 )
912                 ENET_AddMulticastGroup( ethernetifLocal->base, ( uint8_t * ) xLLMNR_MacAdressIPv6.ucBytes );
913             #endif /* ipconfigUSE_LLMNR */
914 
915             for( pxEndPoint = FreeRTOS_FirstEndPoint( pxMyInterface );
916                  pxEndPoint != NULL;
917                  pxEndPoint = FreeRTOS_NextEndPoint( pxMyInterface, pxEndPoint ) )
918             {
919                 if( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED )
920                 {
921                     /* Allow traffic from IPv6 solicited-node multicast MAC address for
922                      * each endpoint */
923                     uint8_t ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
924 
925                     ucMACAddress[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
926                     ucMACAddress[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
927                     ucMACAddress[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
928                     ENET_AddMulticastGroup( ethernetifLocal->base, ucMACAddress );
929                 }
930             }
931         #endif /* ( ipconfigUSE_IPv6 != 0 ) */
932 
933         if( xStatus == kStatus_Success )
934         {
935             FreeRTOS_printf( ( "ENET initialized." ) );
936             ENET_ActiveRead( ethernetifLocal->base );
937         }
938         else
939         {
940             FreeRTOS_printf( ( "Failed to initialize ENET." ) );
941         }
942     }
943 
944     return xStatus;
945 }
946 /*-----------------------------------------------------------*/
947